テスト戦略 レッスン2
単体テストの設計
1つの機能を独立して検証する単体テストの書き方と、テストしやすいコードを設計するコツを学ぶ
良い単体テストの構造: Arrange-Act-Assert
単体テストは準備(Arrange)・実行(Act)・検証(Assert)の 3ステップで構成すると読みやすくなります。1つのテストで1つの振る舞いだけを検証することも重要です。
import { describe, it, expect } from "vitest";
import { calculateShippingFee } from "./shipping";
describe("calculateShippingFee", () => {
it("合計金額が5000円以上なら送料は0円になる", () => {
// Arrange: 準備
const orderTotal = 5000;
// Act: 実行
const fee = calculateShippingFee(orderTotal);
// Assert: 検証
expect(fee).toBe(0);
});
it("合計金額が5000円未満なら送料は500円になる", () => {
const orderTotal = 4999;
const fee = calculateShippingFee(orderTotal);
expect(fee).toBe(500);
});
});テストしやすいコードの条件
テストが書きにくい場合、多くはテスト対象のコード自体に問題があります。 「テストしやすさ」は良い設計のバロメーターでもあります。
// before: 現在時刻・グローバル状態に依存していてテストしづらい
function isDiscountActive() {
const now = new Date(); // テストのたびに結果が変わる
return now.getHours() >= 20;
}
// after: 依存を引数として渡せるようにする(依存性の注入)
function isDiscountActive(currentDate: Date): boolean {
return currentDate.getHours() >= 20;
}
// テストコード: 好きな時刻を注入できるので決定的にテストできる
it("20時以降は割引が有効になる", () => {
const eveningDate = new Date("2026-01-01T20:00:00");
expect(isDiscountActive(eveningDate)).toBe(true);
});現在時刻・乱数・グローバル変数のような「隠れた入力」を関数の引数として明示することで、 テストコードから自由にコントロールできるようになります。
境界値・異常系を意識する
正常系のハッピーパスだけでなく、境界値やエラーケースをテストすることで、 本番で起きがちな「想定外」を事前に潰せます。
境界値の例
- • 空配列・空文字列
- • 0、負の数、最大値
- • ちょうど閾値と等しい値
異常系の例
- • null・undefinedが渡された場合
- • 想定外の型が渡された場合
- • 外部APIがエラーを返した場合
ポイント
- • Arrange-Act-Assertの構造でテストを読みやすくする
- • 1つのテストで1つの振る舞いだけを検証する
- • 現在時刻・乱数などの隠れた入力は引数として明示する(依存性の注入)
- • 正常系だけでなく境界値・異常系もテストする
確認クイズ
1 / 3単体テストの構造として推奨される「Arrange-Act-Assert」の意味として正しいものはどれか?