日本語記事はあまりヒットしなかったので、メモ。
調べたところ、いくつかアプローチはありそうでした。
テスト用のモジュールに組み込んでアプリを起動しe2e的にテストする
function createTestModule(guard) {
return Test.createTestingModule({
imports: [AppModule],
providers: [
{
provide: APP_GUARD,
useValue: guard,
},
],
}).compile();
}
describe('Guards', () => {
let app: INestApplication;
it(`should prevent access (unauthorized)`, async () => {
app = (await createTestModule(new AuthGuard())).createNestApplication();
await app.init();
return request(app.getHttpServer()).get('/hello').expect(401);
});
});
nestjsのサンプルコードのテストになります。
特にモック等を使うこともなく、一番シンプルな方法だと思います。
複数のGuardsを利用する場合等を考えると、実際にリクエストするという点ではユースケースと合わないテストを書くことになります。モジュールの単体テストにする方がより良いと思います。
リクエストを自力でmockして単体テストする
const mockExecutionContext: Partial<
Record<
jest.FunctionPropertyNames<ExecutionContext>,
jest.MockedFunction<any>
>
> = {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn(),
getResponse: jest.fn(),
}),
};
テスト対象となる canActive()
メソッドはbooleanになるためmockに対して何が呼ばれたか期待するテストを書くことになります。
必要なものを必要なだけ毎回自力で作る方法になります。場合によっては冗長になる?
NestJSのテスト用のライブラリを使ってリクエストをmockして単体テストする
import { createMock } from '@golevelup/ts-jest';
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { AuthenticatedGuard } from '@/common/guards';
describe('AuthenticatedGuard', () => {
describe('canActivate', () => {
it('should return true when user is authenticated', () => {
const mockContext = createMock<ExecutionContext>();
mockContext.switchToHttp().getRequest.mockReturnValue({
// method attached to `req` instance by Passport lib
isAuthenticated: () => true,
});
const canActivate = authenticatedGuard.canActivate(mockContext);
expect(canActivate).toBe(true);
});
});
});
利用するのは @golevelup/ts-jest
で、Guardに渡すcontextをmockします。
自力で書くよりはシンプルに書ける印象です。
NestJS公式のライブラリではありませんが、
A collection of Badass modules and utilities to help you level up your NestJS application.
というだけあり、 https://github.com/golevelup/nestjs の配下に ts-jest
の他にも便利そうなライブラリが多くあり興味を持ちました。
実際に利用したもの
3つ目に書いた @golevelup/ts-jest
を利用しました。
JWTを検証・デコードした結果をリクエストに入れて後段に渡したりする場合、1つ目の方法だとセットしたことをテストできません。
また自力でリクエストのmockを書くのもの辛かったため、3つ目の方法を採用しました。
どれにするかは実装やケースによると思いますが、参考になれば。