NestJS(Fastify)+ JestでE2Eテストを実装する
NestJS+ Jestで最低限のE2Eテストを実装する(HTTPアダプターとしてFastifyを使用している前提)。
テスト対象
下記のようなThis is a sample.
という文字列を返すだけのSample
モジュールをテストする。
import { Module } from '@nestjs/common';import { GetSampleUsecase } from './usecases/get-sample.usecase';import { SampleController } from './sample.controller';
@Module({ controllers: [SampleController], providers: [GetSampleUsecase],})export class SampleModule {}
import { Controller, Get } from '@nestjs/common';import { GetSampleUsecase } from './usecases/get-sample.usecase';
@Controller('sample')export class SampleController { constructor(private readonly getSampleUsecase: GetSampleUsecase) {}
@Get() getSample(): string { return this.getSampleUsecase.handle(); }}
export class GetSampleUsecase { handle(): string { return 'This is a sample.'; }}
Jestの設定
/├── src└── test ├── unit └── e2e └── sample
NestJSではデフォルトでユニットテストはsrc
下に、E2Eテストはtest
下に置くようになっているが、個人的な好みとしてどちらもtest
下に配置する。
"jest": { "moduleFileExtensions": [ "js", "json", "ts" ], "rootDir": "test", "testEnvironment": "node", "testRegex": ".*\\..*spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" }, "collectCoverageFrom": [ "**/*.(t|j)s" ], "coverageDirectory": "../coverage",}
テストコード
1. 下準備
import { Test, type TestingModule } from '@nestjs/testing';import { FastifyAdapter, type NestFastifyApplication,} from '@nestjs/platform-fastify';import { mainConfig } from '@src/main.config';import { EnvModule } from '@src/config/env/env.module';import { DatabaseModule } from '@src/config/database/database.module';import type { Type } from '@nestjs/common/interfaces/type.interface';import type { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface';import type { ForwardReference } from '@nestjs/common/interfaces/modules/forward-reference.interface';
type Output = { module: TestingModule; app: NestFastifyApplication;};
export async function setupE2e( imports: ( | Type<any> | DynamicModule | Promise<DynamicModule> | ForwardReference )[],): Promise<Output> { const module = await Test.createTestingModule({ imports, }).compile();
const app = module.createNestApplication<NestFastifyApplication>( new FastifyAdapter(), ); await app.init(); await app.getHttpAdapter().getInstance().ready();
return { module, app };}
E2Eテストで共通となる下準備の処理を実装する。
ここで返されるmodule
とapp
を各E2Eテストで使用し、そのモジュールのプロバイダーを取得したりリクエストを投げたりする。
const module = await Test.createTestingModule({ imports, imports: [...imports, EnvModule, DatabaseModule],}).compile();
もしAppModule
などで環境変数やDB周りのモジュールをimportしている場合は、上記のようにimports
に追加する。
const app = module.createNestApplication<NestFastifyApplication>( new FastifyAdapter(),);app.useGlobalPipes(new ValidationPipe());await app.init();await app.getHttpAdapter().getInstance().ready();
もしグローバルなPipeやGuardをmain.ts
内でバインドしている場合は、上記のようにここで設定しておく。
2. E2Eテストの実装
import { setupE2e } from '../setup-e2e';import type { NestFastifyApplication } from '@nestjs/platform-fastify';import { HttpStatus } from '@nestjs/common';import { SampleModule } from '@src/sample/sample.module';
describe('sample(E2E)', () => { let app: NestFastifyApplication;
beforeAll(async () => { const { module, app: initializedApp } = await setupE2e([SampleModule]); app = initializedApp; });
afterAll(async () => { await app.close(); });
describe('/sample(GET)', () => { it('GETリクエスト_サンプルのテキストが返る', async () => { // run const { statusCode, body } = await app.inject({ method: 'GET', path: 'sample', });
// assert expect(statusCode).toBe(HttpStatus.OK); expect(body).toContain('This is a sample.'); }); });});
あとはそのapp
を使用しリクエストを投げて検証すれば実装完了。
npm run test e2e/sample/sample.e2e-spec.ts
PASS test/e2e/sample/sample.e2e-spec.ts (5.784 s) sample(E2E) /sample(GET) ✓ GETリクエスト_サンプルのテキストが返る (17 ms)
テストを実行し無事成功も確認できた。
let app: NestFastifyApplication;let sampleRepository: SampleRepository;
beforeAll(async () => { const { module, app: initializedApp } = await setupE2e([SampleModule]); app = initializedApp; sampleRepository = module.get<SampleRepository>(SampleRepository);});
もしそのモジュールのプロバイダーを使用したい場合はmodule
からget()
すればよい。
参考