NestJS(Fastify)+ JestでE2Eテストを実装する
NestJS+ Jestで最低限のE2Eテストを実装する(HTTPアダプターとしてFastifyを使用している前提)。
テスト対象
下記のようなThis is a sample.
という文字列を返すだけのSample
モジュールをテストする。
sample/sample.module.tsimport { Module } from '@nestjs/common';
import { GetSampleUsecase } from './usecases/get-sample.usecase';
import { SampleController } from './sample.controller';
@Module({
controllers: [SampleController],
providers: [GetSampleUsecase],
})
export class SampleModule {}
sample/sample.controller.tsimport { 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();
}
}
sample/usecases/get-sample.usecase.tsexport class GetSampleUsecase {
handle(): string {
return 'This is a sample.';
}
}
Jestの設定
/ ├── src └── test ├── unit └── e2e └── sample
NestJSではデフォルトでユニットテストはsrc
下に、E2Eテストはtest
下に置くようになっているが、個人的な好みとしてどちらもtest
下に配置する。
package.json"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. 下準備
e2e/setup-e2e.tsimport { 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テストの実装
e2e/sample/sample.e2e-spec.tsimport { 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()
すればよい。