0
0
NestJSframework~15 mins

E2E testing with supertest in NestJS - Deep Dive

Choose your learning style9 modes available
Overview - E2E testing with supertest
What is it?
E2E testing with supertest means checking if your whole NestJS application works correctly from start to finish. It simulates real user actions by sending HTTP requests to your app and checking the responses. This helps ensure that all parts of your app work together as expected. Supertest is a tool that makes it easy to write these tests by handling HTTP requests and responses.
Why it matters
Without E2E testing, bugs can hide between parts of your app and cause unexpected problems for users. It’s like testing a car by only checking the engine but never driving it. E2E tests catch issues that unit tests miss because they test the app as a whole. This saves time and frustration by finding real problems before users do.
Where it fits
Before learning E2E testing with supertest, you should know basic NestJS app structure and how to write unit tests. After mastering E2E testing, you can explore advanced testing topics like mocking external services or continuous integration pipelines that run tests automatically.
Mental Model
Core Idea
E2E testing with supertest is like pretending to be a user who sends requests to your app and checks if the answers are right, testing the whole system together.
Think of it like...
Imagine ordering food at a restaurant: you place your order (send a request), the kitchen prepares it (your app processes), and the waiter brings your meal (response). E2E testing checks if this whole process works smoothly from order to delivery.
┌─────────────┐       HTTP Request       ┌─────────────┐
│   Test Code │ ───────────────────────▶ │ NestJS App  │
└─────────────┘                         └─────────────┘
       ▲                                         │
       │                                         ▼
       │                               HTTP Response (status, body)
       └─────────────────────────────────────────┘
Build-Up - 8 Steps
1
FoundationUnderstanding E2E Testing Basics
🤔
Concept: Learn what end-to-end testing means and why it tests the whole app flow.
E2E testing checks if your app works from the user's point of view. It sends real HTTP requests to your app and checks the responses. Unlike unit tests that test small parts, E2E tests check the entire system working together.
Result
You understand that E2E tests simulate real user interactions and cover the full app behavior.
Knowing that E2E tests cover the full app helps you see why they catch bugs unit tests miss.
2
FoundationSetting Up NestJS for Testing
🤔
Concept: Prepare a NestJS app to be tested by creating a test module and initializing the app instance.
In NestJS, you create a testing module that mimics your real app module. Then you create an app instance from it and start it in memory. This lets you send requests to it without running a real server.
Result
You can start your NestJS app inside tests and send requests to it.
Understanding how to create a test app instance is key to running E2E tests without a real server.
3
IntermediateUsing Supertest to Send HTTP Requests
🤔Before reading on: Do you think supertest sends requests over the network or directly to the app instance? Commit to your answer.
Concept: Supertest sends HTTP requests directly to the NestJS app instance in memory, not over the network.
Supertest is a library that lets you write code like request(app.getHttpServer()).get('/path').expect(200). It sends HTTP requests to the app instance created in tests and checks the responses easily.
Result
You can write tests that send GET, POST, PUT, DELETE requests and check status codes and response bodies.
Knowing supertest works directly with the app instance makes tests faster and more reliable than real network calls.
4
IntermediateWriting Basic E2E Test Cases
🤔Before reading on: Do you think E2E tests should check only status codes or also response content? Commit to your answer.
Concept: E2E tests check both HTTP status codes and response content to verify correct app behavior.
A basic E2E test sends a request and expects a status code like 200. It also checks the response body to confirm the app returns the right data. For example, testing a GET /users endpoint to return a list of users.
Result
You can write tests that confirm your app returns correct data and status for various endpoints.
Checking response content ensures your app not only responds but responds correctly.
5
IntermediateHandling Setup and Teardown in Tests
🤔
Concept: Learn to prepare test data before tests and clean up after to keep tests independent.
Use beforeAll or beforeEach hooks to set up data or app state before tests run. Use afterAll or afterEach to clean up. This prevents tests from affecting each other and keeps results reliable.
Result
Your tests run consistently and independently without leftover data causing false results.
Managing test lifecycle prevents flaky tests and ensures accurate results.
6
AdvancedTesting with Authentication and Guards
🤔Before reading on: Do you think E2E tests should bypass authentication or test it fully? Commit to your answer.
Concept: E2E tests should include authentication flows to test real user access control.
To test protected routes, your E2E tests must simulate login or provide tokens. This checks if guards and authentication work correctly. For example, first send a login request to get a token, then use it in headers for protected endpoints.
Result
You can test secure parts of your app and confirm only authorized users access them.
Including authentication in E2E tests ensures your app’s security features work as intended.
7
AdvancedMocking External Services in E2E Tests
🤔
Concept: Learn to replace real external services with mocks to isolate tests and avoid side effects.
Sometimes your app calls external APIs or databases. In E2E tests, you can mock these calls to return fixed data. This keeps tests fast and stable. For example, mock a payment gateway response instead of calling the real service.
Result
Your E2E tests run reliably without depending on external systems.
Mocking external services balances test realism with speed and reliability.
8
ExpertOptimizing E2E Tests for Speed and Reliability
🤔Before reading on: Do you think running all E2E tests in one big batch is best, or splitting them smartly? Commit to your answer.
Concept: Experts organize E2E tests to run fast and avoid flaky failures by isolating tests and parallelizing where possible.
Large E2E test suites can be slow. Experts split tests by feature, use test databases reset between runs, and run tests in parallel. They also use retries and detailed logging to diagnose failures quickly.
Result
Your E2E tests run efficiently and provide clear feedback, even in big projects.
Optimizing test structure and environment is crucial for maintaining fast, trustworthy E2E tests in production.
Under the Hood
Supertest works by wrapping your NestJS app's HTTP server instance and sending simulated HTTP requests directly to it in memory. This avoids real network calls, making tests faster and more controlled. NestJS creates a test module that mimics the real app, including controllers, services, and middleware. When a test sends a request, NestJS processes it through the full request lifecycle, including routing, guards, pipes, and interceptors, just like in production. The response is captured and returned to the test code for assertions.
Why designed this way?
This design allows fast, reliable tests without needing a real network or server process. It avoids flakiness caused by network issues and speeds up test runs. Alternatives like spinning up a real server or using HTTP clients over the network are slower and less reliable. NestJS’s modular architecture makes it easy to create isolated test modules that behave like the real app but run entirely in memory.
┌───────────────┐
│ Test Code     │
│ (supertest)   │
└──────┬────────┘
       │ sends HTTP request
       ▼
┌───────────────┐
│ NestJS Test   │
│ App Instance  │
│ (in-memory)   │
└──────┬────────┘
       │ processes request
       ▼
┌───────────────┐
│ Controllers & │
│ Middleware    │
│ Services      │
└──────┬────────┘
       │ sends HTTP response
       ▼
┌───────────────┐
│ Test Code     │
│ receives resp │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think E2E tests only check if endpoints respond, ignoring internal logic? Commit yes or no.
Common Belief:E2E tests just check if endpoints respond with status codes, not the internal app logic.
Tap to reveal reality
Reality:E2E tests run the full app logic including controllers, services, guards, and middleware, verifying the entire request lifecycle.
Why it matters:Ignoring internal logic means missing bugs in business rules or security that only show up when the full app runs.
Quick: Do you think supertest sends requests over the real network? Commit yes or no.
Common Belief:Supertest sends HTTP requests over the network like a browser or Postman.
Tap to reveal reality
Reality:Supertest sends requests directly to the app instance in memory without network overhead.
Why it matters:Thinking it uses the network leads to slower tests and confusion about test isolation.
Quick: Do you think E2E tests should always mock external services? Commit yes or no.
Common Belief:E2E tests must always use real external services to be accurate.
Tap to reveal reality
Reality:Mocking external services in E2E tests is common to keep tests fast and reliable, while still testing app integration.
Why it matters:Using real external services can cause flaky tests due to network issues or service downtime.
Quick: Do you think E2E tests replace unit tests completely? Commit yes or no.
Common Belief:E2E tests are enough; unit tests are unnecessary if E2E tests exist.
Tap to reveal reality
Reality:E2E tests complement but do not replace unit tests; unit tests catch small bugs faster and isolate logic.
Why it matters:Relying only on E2E tests can slow development and make debugging harder.
Expert Zone
1
E2E tests can reveal integration issues caused by middleware order or global pipes that unit tests miss.
2
Properly resetting the test database state between tests is crucial to avoid hidden dependencies and flaky failures.
3
Using supertest with NestJS’s fastify adapter requires special setup because of different server internals.
When NOT to use
E2E testing with supertest is not ideal for testing isolated business logic or small units of code; use unit tests instead. Also, for UI behavior, use dedicated frontend testing tools like Cypress. When external services are complex and slow, consider contract testing or integration tests instead of full E2E.
Production Patterns
In real projects, E2E tests run in CI pipelines on every code push to catch regressions early. Teams organize tests by feature areas and use test databases reset via migrations or fixtures. Authentication flows are tested by automating login and token handling. External APIs are mocked with tools like nock or custom mocks to keep tests stable.
Connections
Unit Testing
complements
Understanding E2E testing helps appreciate why unit tests focus on small parts, while E2E tests check the whole app working together.
Continuous Integration (CI)
builds-on
E2E tests are often run automatically in CI pipelines to ensure code changes don’t break the app, linking testing to deployment safety.
Restaurant Order Process
similar process flow
Seeing E2E testing as a full order-to-delivery process helps understand the importance of testing every step in a system.
Common Pitfalls
#1Running E2E tests without resetting the database causes tests to interfere with each other.
Wrong approach:beforeAll(() => { // no database cleanup }); it('test 1', () => { /* test code */ }); it('test 2', () => { /* test code */ });
Correct approach:beforeEach(async () => { await resetDatabase(); }); it('test 1', () => { /* test code */ }); it('test 2', () => { /* test code */ });
Root cause:Not isolating test data causes leftover data to affect later tests, leading to flaky failures.
#2Skipping authentication in E2E tests for protected routes leads to false positives.
Wrong approach:request(app.getHttpServer()).get('/protected').expect(200); // no auth token
Correct approach:const token = await loginAndGetToken(); request(app.getHttpServer()).get('/protected').set('Authorization', `Bearer ${token}`).expect(200);
Root cause:Ignoring auth means tests don’t verify real access control, missing security bugs.
#3Using real external APIs in E2E tests causes slow and unreliable tests.
Wrong approach:appService.callRealPaymentGateway(); // in E2E test
Correct approach:jest.mock('paymentGateway', () => ({ processPayment: () => Promise.resolve({ success: true }) }));
Root cause:External dependencies introduce network delays and failures, making tests flaky.
Key Takeaways
E2E testing with supertest checks your entire NestJS app by simulating real user HTTP requests and verifying responses.
Supertest sends requests directly to the app instance in memory, making tests fast and reliable without real network calls.
Writing good E2E tests means checking both status codes and response content, including authentication and protected routes.
Managing test setup and teardown prevents flaky tests by keeping each test independent and isolated.
Experts optimize E2E tests by mocking external services, organizing tests smartly, and running them efficiently in CI pipelines.