@nestjs/testing builds a module the same way the application does, and lets you replace any provider in it — the payoff for accepting dependency injection. One line does it: Test.createTestingModule({ providers: [BooksService, Store] }).overrideProvider(Store).useValue(stub).
End-to-end tests go further and build the whole application in memory, driven with Supertest 14,405 as in Docs and Tests. createNestApplication() never listens on a port, so the suite needs no free port and no teardown but app.close().
beforeEach(async () => {
const mod = await Test.createTestingModule({ imports: [AppModule] }).compile();
app = configure(mod.createNestApplication({ logger: false })); // same pipes as main.ts
await app.init();
});
it('refuses an anonymous POST with 401 and a reader with 403', async () => {
const body = { title: 'Dawn', authorId: 'au_3', year: 1987, genre: 'sci-fi' };
await request(app.getHttpServer()).post('/api/v1/books').send(body).expect(401);
const res = await request(app.getHttpServer()).post('/api/v1/books')
.set('Authorization', `Bearer ${reader}`).send(body).expect(403);
expect(res.body.error.message).toBe('Requires one of: editor'); });The trap is that configure(app) line: call it in the test too, or the global validation pipe is missing and you are testing an application you do not ship.
$ npx vitest run --config ./vitest.config.e2e.ts --reporter=verbose ✓ test/bookshelf.e2e-spec.ts > Bookshelf (e2e) > GET /api/v1/books pages the catalog 215ms ✓ rejects an out-of-range limit with 422 and lists every problem 27ms ✓ refuses an anonymous POST with 401 and a reader with 403 23ms ✓ accepts one review per reader, and answers 409 for the second 21ms Test Files 1 passed (1) Tests 6 passed (6) Duration 2.16s
A sixth case asserts on the generated document — expect(Object.keys(doc.paths)).toContain( '/api/v1/books/{id}/reviews'). Two lines, and it catches the failure nobody notices: a route that still works but dropped out of the published contract. A fresh testing module per test also gives a fresh Store singleton, so the cases share no state and run in any order.