モンスターラボのエンジニアリングマネージャーの奥田です。
AIによりコードが大量に生成されるようになった今、ソフトウェアの品質担保はこれまで以上に重要なテーマとなりました。品質担保のためにテストコードを書くというのは多くの開発現場で行われていますし、テストコードも生成AIが書けるようになったことでテストカバレッジは大幅に向上していると思います。
一方で、大量に生成されたプロダクトコード、テストコードをチェックするのは大変な作業ですし、AIなどのツールを利用するにしてもガードレールとチェックポイントを適切に設定する必要があります。
本記事では、ゴールデンテスト(Golden Test) というテスト手法を紹介し、AI時代のソフトウェア開発における実践的な適用方法と、AI駆動開発との相性の良さについて解説します。
ゴールデンテストとは
ゴールデンテストは、テストコードの出力を「ゴールデンファイル」と呼ばれる期待値ファイルと比較するテスト手法です。VRT(Visual Regression Test)と呼ばれる視覚的な正しさを担保する回帰テストなどで使われることが多いと思います。 通常のユニットテストでは期待値をコード内にアサーションとして記述しますが、ゴールデンテストでは期待値を外部ファイルとして管理し、テスト実行時に実際の出力とファイルの中身を比較します。
基本的な流れ
- テスト対象の関数/APIを実行し、出力を取得する
- ゴールデンファイル(期待値ファイル)が存在しない場合 → 出力をそのまま保存する
- ゴールデンファイルが存在する場合 → 出力とファイルの内容を比較する
- 差分がある場合 → テスト失敗として差分を報告する
- 意図した変更の場合 → ゴールデンファイルを更新してコミットする
通常のユニットテストとの違い
| 観点 | ユニットテスト | ゴールデンテスト |
|---|---|---|
| 期待値の管理場所 | テストコード内 | 外部ファイル |
| 期待値の作成 | 手動で記述 | 初回実行時に自動生成可能 |
| 大きな出力への対応 | アサーションが膨大になる | ファイル比較で簡潔に対応 |
| 変更検知 | 明示的にチェックした箇所のみ | 出力全体の変更を検知 |
| 期待値の更新 | テストコードを手動で修正 | ファイルを更新してコミット |
ゴールデンテストの何がいいのか
テストコードをシンプルに保ちやすい
関数やAPIの入力を定義し、出力はゴールデンファイルと比較するだけなのでテストコードをシンプルに保ちやすいです。
リファクタリング時のテスト更新コストが低い
コードをリファクタリングした結果、出力のフォーマットが微妙に変わることがあります。個別のアサーションを大量に持つテストではどこが変わったのかを把握し、失敗するアサーションに適切に対処する必要があります。そもそも、適切なアサーションがなければ出力の微妙な変化に気づけません。ゴールデンテストであれば、ゴールデンファイルの差分により確実に差分を検出できますし、ゴールデンテストの差分に問題がなければコミットするだけでテストを更新できます。
人がレビューしやすい
GitHub上のプルリクエストなどでコード差分をレビューする際、ゴールデンファイルを見るだけで差分があるのかないのか、あるとしたらどこなのかをすぐに確認することができます。ゴールデンファイルが画像であってもテキストであっても、レビューしやすく便利です。
# git diff で出力の変更が一目瞭然
- {"status": "ok", "data": {"userId": 1, "name": "Taro"}}
+ {"status": "ok", "data": {"userId": 1, "name": "Taro", "email": "taro@example.com"}}
「何が変わったか」をアサーションの修正履歴ではなく、出力の差分として直接把握できるため、レビューの効率が劇的に向上します。
AIが意味のないテストケースを生成しにくい
AI が生成するテストコードは、テストを通すための無意味なアサーションや不適切なモックを使いがちです。大量の無意味なテストケースにより本質的に確認すべきポイントが見失われ、テストコード自体のメンテナンスコストが上昇し、テストを書く効率が落ちる一方で品質が向上しないという本末転倒な事態にも陥りかねません。 ゴールデンテストの場合、ゴールデンファイルが出力の期待値であるため、AIが期待値を「捏造」するリスクがありません。
ゴールデンファイルは仕様書にもなる
入力と出力を適切に出力すれば、ゴールデンファイルは仕様書にもなりえます。
私が過去に担当したプロジェクトでは、OpenAPIなどでAPI仕様書を記述せず、ゴールデンファイルにリクエスト、レスポンスを出力することでAPI仕様書として管理していました。
また別のプロジェクトでは、生成AIに投げるシステムプロンプトをユーザ状態に応じて合成して生成していたので、そのシステムプロンプトのテストをゴールデンテストで記述しました。ユーザ状態ごとに生成されたシステムプロンプトをゴールデンファイルで確認できるので便利でした。
実装から出力されるのでコードとの乖離もなく、プロジェクト次第では仕様書として十分機能すると思います。
ゴールデンテストが向いているテスト
API レスポンスの回帰テスト
REST API や GraphQL のリクエスト/レスポンスは、構造が複雑でフィールド数も多くなりがちです。個々のフィールドに対してアサーションを書くよりも、レスポンス全体をゴールデンファイルとして管理する方が効率的です。またゴールデンファイルにリクエスト、レスポンスを出力することでAPIの仕様書として扱うこともできます。
データ変換処理の検証
CSV → JSON、XML → オブジェクトなど、入出力の変換処理は出力が大きく予測しづらいケースがあります。ゴールデンテストなら、変更差分を簡単にチェックできます。
設定ファイル・テンプレートの生成
Infrastructure as Code のテンプレート(CloudFormation、Terraform)や設定ファイルの生成結果を検証する際にも有効です。
適用に注意が必要なケース
- 非決定的な出力(タイムスタンプ、UUID、ランダム値)を含む場合は、マスキングや正規化が必要です
- 環境依存の出力(ホスト名、パスなど)がある場合は、テスト環境を固定するか出力をサニタイズする
- そもそもゴールデンファイルとの差分を検知するため回帰テスト向きです。新たにテストケースを作成した場合はゴールデンファイルに問題がないかしっかり確認する必要があります
導入方法
ここでは、TypeScript(Jest)を使ったバックエンドAPIのゴールデンテスト導入例を示します。
Step 1: ゴールデンテストユーティリティの作成
// src/test-utils/golden.ts
import * as fs from "fs";
import * as path from "path";
const GOLDEN_DIR = path.resolve(__dirname, "../../__golden__");
const UPDATE_GOLDEN = process.env.UPDATE_GOLDEN === "true";
/**
* 実際の出力をゴールデンファイルと比較する。
* ゴールデンファイルが存在しない場合、または UPDATE_GOLDEN=true の場合はファイルを更新する。
*/
export function expectMatchesGoldenFile(
testName: string,
actual: unknown
): void {
const sanitized = sanitizeOutput(actual);
const content = JSON.stringify(sanitized, null, 2) + "\n";
const filePath = path.join(GOLDEN_DIR, `${testName}.json`);
if (!fs.existsSync(GOLDEN_DIR)) {
fs.mkdirSync(GOLDEN_DIR, { recursive: true });
}
if (UPDATE_GOLDEN || !fs.existsSync(filePath)) {
fs.writeFileSync(filePath, content, "utf-8");
if (!fs.existsSync(filePath)) {
console.log(`Golden file created: ${filePath}`);
}
return;
}
const expected = fs.readFileSync(filePath, "utf-8");
expect(content).toBe(expected);
}
/**
* 非決定的な値(タイムスタンプ、UUIDなど)をマスキングする
*/
function sanitizeOutput(obj: unknown): unknown {
if (obj === null || obj === undefined) return obj;
if (typeof obj === "string") {
return obj
.replace(
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/g,
"<<TIMESTAMP>>"
)
.replace(
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,
"<<UUID>>"
);
}
if (Array.isArray(obj)) return obj.map(sanitizeOutput);
if (typeof obj === "object") {
const result: Record<string, unknown> = {};
for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
result[key] = sanitizeOutput(value);
}
return result;
}
return obj;
}
Step 2: API ハンドラーのゴールデンテストを書く
// src/handlers/__tests__/getUser.golden.test.ts
import { expectMatchesGoldenFile } from "../../test-utils/golden";
import { handler } from "../getUser";
describe("GET /users/:id - Golden Tests", () => {
it("正常系: ユーザー取得", async () => {
const event = {
pathParameters: { id: "user-001" },
headers: { authorization: "Bearer test-token" },
};
const response = await handler(event);
expectMatchesGoldenFile("getUser/success", {
statusCode: response.statusCode,
body: JSON.parse(response.body),
});
});
it("異常系: 存在しないユーザー", async () => {
const event = {
pathParameters: { id: "non-existent" },
headers: { authorization: "Bearer test-token" },
};
const response = await handler(event);
expectMatchesGoldenFile("getUser/not-found", {
statusCode: response.statusCode,
body: JSON.parse(response.body),
});
});
});
Step 3: npm scripts の設定
{
"scripts": {
"test": "jest",
"test:update-golden": "UPDATE_GOLDEN=true jest"
}
}
Step 4: ゴールデンファイルの運用
# 初回実行: ゴールデンファイルが自動生成される
npm run test:update-golden
# 生成されたファイルを確認
cat __golden__/getUser/success.json
# 内容に問題がなければコミット
git add __golden__/
git commit -m "Add golden files for getUser API"
# 以降は通常のテストとして実行
npm test
# コード変更後にゴールデンファイルを更新する場合
npm run test:update-golden
git diff __golden__/ # 差分を確認
git add __golden__/ && git commit -m "Update golden files"
生成されるゴールデンファイルの例
// __golden__/getUser/success.json
{
"statusCode": 200,
"body": {
"id": "user-001",
"name": "山田太郎",
"email": "taro@example.com",
"createdAt": "<<TIMESTAMP>>",
"updatedAt": "<<TIMESTAMP>>"
}
}
// __golden__/getUser/not-found.json
{
"statusCode": 404,
"body": {
"error": {
"code": "USER_NOT_FOUND",
"message": "指定されたユーザーが見つかりません"
}
}
}
まとめ
ゴールデンテストはシンプルですが、AI駆動開発が主流となった現代でも活用できるテスト手法だと思います。
AIがコードを書く時代だからこそ、「何が変わったか」を確実に可視化できる仕組みが重要です。ゴールデンテストは、その仕組みを最小限のコストで実現する、実践的な選択肢と言えるでしょう。
Author

Shuhei Okuda
エンジニアリングマネージャー/バックエンド/テックリード


