Build and test a production-style authentication application (Next.js + Express API) with Jest, RTL, Supertest, Playwright, and GitHub Actions.
Module 7 — Capstone Project
Who this is for: Every student who wants to solidify their knowledge. Watching tutorials is passive; writing tests against a complex codebase is active learning.
The Challenge: A Production-Ready Testing Suite
In this final module, you will not be writing an application from scratch. Instead, you will be handed a fully functioning, undocumented, untested full-stack application.
Your mission is to wrap this application in a bulletproof testing net, ensuring that future developers can refactor it without fear.
The Application: SecureAuth Pro
SecureAuth Pro is a Next.js (frontend) and Express.js (backend) application backed by PostgreSQL. It features user registration, JWT-based login, password resets, and a protected dashboard.
Project Deliverables
You must implement the entire Testing Pyramid and automate it.
Phase 1: Unit & Integration Testing (Jest & Supertest)
You will focus on the Node.js backend.
- Middleware Testing: Write unit tests for the JWT authentication middleware. Mock the
jsonwebtokenlibrary. Ensure it returns401 Unauthorizedfor expired tokens. - Database Mocking: Write unit tests for the
UserRepository. Mock the Prisma/Sequelize database driver to test successful user creation and constraint violations (duplicate emails). - API Integration: Use Supertest to spin up the Express app in-memory. Write a test suite for
POST /api/auth/login. Assert the response contains a valid token structure when correct credentials are provided.
Phase 2: Frontend Component Testing (React Testing Library)
You will focus on the Next.js frontend.
- Form Validation: Render the
<RegistrationForm />. Use RTL to fill the inputs. Assert that clicking submit with an invalid email format renders an error boundary on the screen. - API Mocking: Render the
<Dashboard />component. Mock the globalfetchAPI to simulate a successful data retrieval. Assert that the user's data renders correctly on the screen after the loading state resolves.
Phase 3: End-to-End Testing (Playwright)
You will test the system exactly as a real user would.
- The Golden Path: Write a Playwright script that navigates to the homepage, clicks "Sign Up", fills the form, submits, logs in, and verifies the dashboard loads.
- Auth Bypass: Implement a
global-setup.jsscript to save the login state. Write a second test that navigates directly to/dashboardutilizing the saved state, proving it bypasses the login screen. - Network Interception: Intercept the backend API call on the login page. Force it to return a
500 Internal Server Error. Assert that the UI gracefully displays a toast notification to the user rather than crashing.
Phase 4: Automation (GitHub Actions)
You will protect the main branch.
- CI Pipeline: Create a
.github/workflows/main.ymlfile. Configure it to run the Jest suite and the Playwright suite on every pull request. - Coverage Gates: Configure Jest to fail the pipeline if branch coverage drops below
80%.
The Ultimate Goal
When you complete this capstone, you will have a portfolio-quality project demonstrating true full-stack testing capabilities.
More importantly, you will possess the engineering mindset required to join a production team, audit their testing architecture, and drastically improve their software reliability.
Key Takeaways
- Real-world testing requires balancing different tools (Jest, RTL, Supertest, Playwright) across the full stack.
- E2E tests are powerful but should be used sparingly for critical user journeys (like Auth).
- Database mocking speeds up unit tests, but real integration tests are needed for full confidence.
- A fully automated CI/CD pipeline is the ultimate deliverable of a strong testing strategy.
Knowledge Check
Question 1: In Phase 1 of the Capstone project, you are instructed to use Jest to unit test the JWT authentication middleware. Why is unit testing (with a mocked jsonwebtoken library) the correct strategy here, rather than relying solely on Supertest integration tests?
- A) Supertest cannot send HTTP headers, so it is impossible to pass a
Bearertoken to the Express routes during an integration test. - B) Unit tests provide rapid, highly isolated feedback. By mocking the JWT library, you can instantly simulate edge cases (like expired or malformed tokens) without needing to actually generate those invalid tokens or interact with the database.
- C) JWT tokens require a real browser to be verified, meaning unit tests are the only way to test them outside of Playwright E2E tests.
- D) Jest is automatically integrated with Express middleware, whereas Supertest only works with final route controllers.
Reveal Answer
Correct Answer: B
Middleware functions are perfect candidates for isolated unit testing. By mocking the external dependency (jsonwebtoken), you gain absolute control over the test scenario. You can easily force the mock to throw a TokenExpiredError and verify that your middleware catches it and correctly calls res.status(401). Relying solely on integration tests for this would be slow and cumbersome, requiring you to generate actual tokens, wait for them to expire, and fire off full HTTP requests just to test a single conditional branch.
Question 2: In Phase 3, you write a Playwright E2E test that intercepts the network request to /api/auth/login and forces it to return a 500 Internal Server Error. What is the primary architectural value of this specific test?
- A) It guarantees that the backend Node.js server will never crash when a user tries to log in.
- B) It verifies that the Postgres database is properly isolated from the authentication service during high-load scenarios.
- C) It proves the frontend application handles backend API failures gracefully (e.g., displaying a user-friendly error message) rather than freezing, crashing, or displaying raw JSON to the user.
- D) It checks the CI/CD pipeline's ability to rollback a deployment when a 500 error is detected in production.
Reveal Answer
Correct Answer: C
Network interception in E2E testing is crucial for testing the frontend's resilience. It's often impossible or highly impractical to intentionally cause a real backend to throw a 500 error on demand during an automated test. By using Playwright to intercept the request and simulate the 500 error from the browser's perspective, you can confidently assert that the React UI reacts correctly—showing a toast notification or error boundary—thus protecting the end user's experience when inevitably, external systems fail.
Question 3: Phase 4 of the Capstone requires configuring GitHub Actions to run the test suite and enforce a coverage gate on every Pull Request. Which foundational principle of the "Testing Pyramid" strategy does this automation enforce?
- A) The principle that E2E tests should form the broad base of the pyramid because they are the most reliable.
- B) The principle that human code reviews are unnecessary if test coverage is above 80%.
- C) The principle that tests are only a "safety net" if they are executed consistently and impartially; without CI blocking bad PRs, the test suite will eventually rot as developers skip running it locally.
- D) The principle that unit tests are inherently slower than integration tests and require cloud runners to execute efficiently.
Reveal Answer
Correct Answer: C
The entire effort spent building a comprehensive testing strategy (Unit, Integration, E2E) is wasted if developers can simply merge code without running the tests. An automated CI pipeline with quality gates (like coverage thresholds) ensures the Testing Pyramid is permanently enforced. It mechanically rejects broken or untested code, ensuring the main branch remains stable and that the test suite is actively maintained and respected by the entire engineering team.
Congratulations on completing the Master JavaScript Testing course.
Discussion
0Join the discussion