End-to-End (E2E) Testing
End-to-End (E2E) Testing
End-to-end (E2E) testing boots your entire NestJS application inside a test environment and fires real HTTP requests through it, verifying that every layer — routing, guards, pipes, services, and the database — works together correctly. Unlike unit tests that isolate a single class, E2E tests give you confidence that the wired-up application behaves correctly from the outside world's perspective.
Tools: Supertest + Jest
NestJS's scaffolding ships a test/ directory with a ready-made E2E setup. The two key pieces are:
- Supertest — a fluent HTTP assertion library. It accepts an Express (or Fastify) HTTP server and lets you make requests and assert on status codes, headers, and bodies without ever binding to a real port.
- Jest — the test runner. NestJS includes a separate
jest-e2econfig inpackage.jsonthat pointsrootDirattest/and setstestRegexto.e2e-spec.ts.
npm run test:e2e. This invokes jest --config ./test/jest-e2e.json, which is separate from your unit-test run (npm test). Keep the two suites separate so fast unit tests are not slowed down by database setup.
Booting the full application in a test module
Use Test.createTestingModule() — the same API as unit tests — but import your real AppModule instead of a minimal slice. Calling .compile() then app.init() starts the NestJS lifecycle (module init, onApplicationBootstrap hooks, etc.):
main.ts registers a ValidationPipe, CORS, or a global prefix, apply the same configuration inside beforeAll. Forgetting this causes tests to pass locally but fail against the real app.
Test database setup and teardown
Running E2E tests against your production or development database is dangerous. The standard approach is to point the test run at a dedicated test database via environment variables, then clean up after each test or suite:
- Set
NODE_ENV=testand create a.env.test(or use a separate TypeORM config) that targets a test-only database or an in-memory SQLite database. - Run migrations before the suite starts (or use
synchronize: trueonly for tests). - Truncate or roll back data between tests to avoid order-dependent failures.
Testing auth-protected endpoints end to end
A complete E2E auth flow typically has two phases: obtain a token, then use it. Supertest chains make this readable:
What to assert
- Status codes — the most basic check;
.expect(200),.expect(401),.expect(422). - Response shape — assert that required fields exist and have the expected types.
- Side effects — after a POST that creates a resource, query the database directly (via the injected repository) to confirm the row was persisted.
- Guard enforcement — always include a negative test: hitting a protected route without a token must return 401.
describe block) should seed its own prerequisite data and clean it up afterward. Tests that depend on prior tests running first are fragile and hard to debug.
Summary
E2E tests in NestJS use Test.createTestingModule() with the real AppModule and Supertest to fire HTTP requests against the running application. Point tests at a dedicated test database, run migrations in beforeAll, and tear down in afterAll. For auth-protected routes, obtain a JWT in a setup step then attach it via the Authorization header. Always include negative tests (no token → 401) and verify database side-effects for mutation endpoints.