End-to-End Testing

Boot the Real App, Hit It With supertest

End-to-End Testing

e2e tests start the full module tree and make real HTTP calls — they catch the wiring bugs unit tests miss.

4 min read Level 2/5 #nestjs#testing#e2e
What you'll learn
  • Set up an e2e Jest config under test/
  • Boot the app with createTestingModule + createNestApplication
  • Use supertest to assert on real responses

Unit tests prove your services do what you think. End-to-end tests prove the whole module tree actually wires up — routes, pipes, guards, filters and all. nest new ships an e2e setup already.

The Layout

test/
  app.e2e-spec.ts
  jest-e2e.json

jest-e2e.json points Jest at *.e2e-spec.ts files and uses a separate config from your unit tests.

A Minimal e2e Spec

import { INestApplication, ValidationPipe } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';

describe('Users (e2e)', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleRef.createNestApplication();
    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
    await app.init();
  });

  afterAll(() => app.close());

  it('GET /users -> []', () => {
    return request(app.getHttpServer())
      .get('/users')
      .expect(200)
      .expect([]);
  });

  it('POST /users validates the body', () => {
    return request(app.getHttpServer())
      .post('/users')
      .send({ email: 'not-an-email' })
      .expect(400);
  });
});

app.getHttpServer() returns the underlying Node HTTP server, which supertest can call without binding a port.

Use A Test Database

Run migrations against a throwaway database before the suite, then truncate between tests. Docker Compose plus a globalSetup file is a common pattern:

// test/global-setup.ts
import { execSync } from 'node:child_process';
export default async () => {
  execSync('npm run db:migrate', { stdio: 'inherit' });
};

Override Providers For Speed

You do not have to use the real database. Swap any provider with a mock using .overrideProvider(MailService).useValue(stub) on the testing module builder — perfect for outbound integrations.

Performance Tuning →