このチュートリアルで学ぶこと
- Jestのセットアップ
- 基本的なテストの書き方
- マッチャー(アサーション)
- モックとスパイ
- 非同期テスト
- カバレッジレポート
Step 1: セットアップ
実践メモ: ts-jestを使うとTypeScriptのテストを直接実行できます。jest.config.jsでpreset: 'ts-jest'を指定しましょう。
npm init -y
npm install -D jest @types/jest ts-jest typescript
npx ts-jest config:init
jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
collectCoverageFrom: ['src/**/*.ts'],
};
Step 2: 基本的なテスト
// src/math.ts
export function add(a: number, b: number): number {
return a + b;
}
export function divide(a: number, b: number): number {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
// src/math.test.ts
import { add, divide } from './math';
describe('Math functions', () => {
describe('add', () => {
it('should add two numbers', () => {
expect(add(1, 2)).toBe(3);
});
it('should handle negative numbers', () => {
expect(add(-1, 1)).toBe(0);
});
});
describe('divide', () => {
it('should divide two numbers', () => {
expect(divide(10, 2)).toBe(5);
});
it('should throw error for division by zero', () => {
expect(() => divide(10, 0)).toThrow('Division by zero');
});
});
});
Step 3: マッチャー
// 等値
expect(value).toBe(3); // 厳密等価
expect(obj).toEqual({ a: 1 }); // オブジェクト比較
// 真偽値
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeDefined();
// 数値
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(5);
expect(0.1 + 0.2).toBeCloseTo(0.3);
// 文字列
expect(str).toMatch(/pattern/);
expect(str).toContain('substring');
// 配列
expect(arr).toContain(item);
expect(arr).toHaveLength(3);
Step 4: モック
ポイント: `jest.mock()`は外部依存(DB、API)を分離するために利用できます。テスト対象の関数自体をモックしないよう注意しましょう。
注意: 必要に応じて各テスト後に`jest.clearAllMocks()`などでモックをリセットし、テスト間の状態漏れを防ぎます。
// src/userService.ts
import { db } from './db';
export async function getUser(id: number) {
return db.user.findById(id);
}
// src/userService.test.ts
import { getUser } from './userService';
import { db } from './db';
jest.mock('./db');
describe('UserService', () => {
it('should return user', async () => {
const mockUser = { id: 1, name: 'Test' };
(db.user.findById as jest.Mock).mockResolvedValue(mockUser);
const result = await getUser(1);
expect(result).toEqual(mockUser);
expect(db.user.findById).toHaveBeenCalledWith(1);
});
});
Step 5: 非同期テスト
// async/await
it('should fetch data', async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
// Promises
it('should resolve', () => {
return expect(asyncFunc()).resolves.toBe('value');
});
it('should reject', () => {
return expect(asyncFunc()).rejects.toThrow();
});
Step 6: テスト実行
# テスト実行
npm test
# ウォッチモード
npm test -- --watch
# カバレッジ
npm test -- --coverage
テストで確認する対象
Jestで大切なのは、実装の細部ではなく振る舞いを確認することです。関数の内部でどの変数を使ったかより、入力に対して期待した出力や副作用が得られるかを見るほうが、リファクタリングに強いテストになります。
最初は正常系だけでもよいですが、慣れてきたら異常系、境界値、空配列、null相当の入力なども確認します。テスト名には「何をしたら、何が起きるか」を書くと、失敗時に原因を追いやすくなります。
書き方の基準
- 1つのテストで1つの振る舞いを見る
- モックしすぎて実際の動きから離れないようにする
- 日付や乱数は固定して再現性を保つ
- 失敗したときのメッセージが読める粒度にする
まとめ
Jestは使いやすいテストフレームワークで、モック機能も充実しています。テストを書くことでコードの品質と信頼性が向上します。