← 解説「ユニットテスト — Test.createTestingModule」に戻る
サンプルコードで身につける: ユニットテスト — Test.createTestingModule
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1テスト用モジュールを組み立てて取り出す
Test.createTestingModule({ providers }) でテスト専用の DIコンテナを作り、.compile() してから get で対象を取り出す、という最小の骨格です。本番の AppModule を起動せずにサービス単体を取り出せるのが要点です。
import { Test, TestingModule } from "@nestjs/testing";
import { CalcService } from "./calc.service";
describe("CalcService", () => {
let service: CalcService;
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [CalcService], // 依存のないサービスはそのまま
}).compile();
service = moduleRef.get<CalcService>(CalcService);
});
it("adds two numbers", () => {
expect(service.add(2, 3)).toBe(5);
});
});
2依存を useValue でモックに差し替える
テスト対象が依存するリポジトリを { provide, useValue: モック } で偽物に差し替える定番です。本物の DB に触れずに、モックの戻り値を起点にサービスのロジックだけを検証できます。
import { Test, TestingModule } from "@nestjs/testing";
import { UsersService } from "./users.service";
import { UsersRepository } from "./users.repository";
describe("UsersService", () => {
let service: UsersService;
const repoMock = { findOne: jest.fn() }; // 依存はモック
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
UsersService, // テスト対象は本物
{ provide: UsersRepository, useValue: repoMock },
],
}).compile();
service = moduleRef.get<UsersService>(UsersService);
});
it("finds a user by id", async () => {
repoMock.findOne.mockResolvedValue({ id: 1, name: "Alice" });
const user = await service.findOne(1);
expect(user.name).toBe("Alice");
});
});
3「対象は本物・依存はモック」をプレーンTSで再現
ユニットテストの線引き「テスト対象そのものは本物、それが依存するものだけモック」を、デコレータなしで再現した実行例です。モックに仕込んだ戻り値を起点に、本物のサービスのロジックが検証されている様子が分かります。
TypeScript
4overrideProvider で一部だけ差し替える
既存モジュールの構成をそのまま流用しつつ、一部の依存だけを overrideProvider(Token).useValue(モック) で差し替える書き方です。providers をゼロから並べ直さずに済むため、依存の多い機能モジュールのテストで重宝します。
import { Test, TestingModule } from "@nestjs/testing";
import { UsersModule } from "./users.module";
import { MailService } from "./mail.service";
import { UsersService } from "./users.service";
describe("UsersService (module 流用)", () => {
let service: UsersService;
const mailMock = { send: jest.fn() };
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [UsersModule], // 既存構成をそのまま使う
})
.overrideProvider(MailService) // メール送信だけモックに
.useValue(mailMock)
.compile();
service = moduleRef.get(UsersService);
});
it("registers without sending real mail", async () => {
await service.register("alice@example.com");
expect(mailMock.send).toHaveBeenCalled();
});
});
5実務: 外部依存をモックしたサービステスト
外部メール送信クライアントに依存するユーザー登録サービスを、クライアントをモックして検証する実務例です。本物のメールを送らずに「登録時に確認メールが送られたか」をモックの呼び出し履歴で確認するのが定石です。
import { Test, TestingModule } from "@nestjs/testing";
import { SignupService } from "./signup.service";
import { MailClient } from "./mail.client";
import { UsersRepository } from "./users.repository";
describe("SignupService", () => {
let service: SignupService;
const mailMock = { sendWelcome: jest.fn() };
const repoMock = { save: jest.fn().mockResolvedValue({ id: 1 }) };
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
SignupService, // 対象は本物
{ provide: MailClient, useValue: mailMock }, // 外部依存はモック
{ provide: UsersRepository, useValue: repoMock },
],
}).compile();
service = moduleRef.get(SignupService);
});
it("saves the user and sends a welcome mail", async () => {
await service.signup("alice@example.com");
expect(repoMock.save).toHaveBeenCalled();
// 本物のメールは送らずに送信されたことだけ検証
expect(mailMock.sendWelcome).toHaveBeenCalledWith("alice@example.com");
});
});