ゴールデンファイルテスト
Spec: ISO/IEC/IEEE 29119-4 ISO/IEC/IEEE 29119-4 Spec: ISO/IEC 25010 ISO/IEC 25010 Evidence: Test-backed
ゴールデンファイルとは、「正しい出力とはこういうものだ」と記録したものであり、テストは実行のたびにこれと照合します。NextPDF はゴールデンを使って、誰も意図しなかった変更を検出します。圧縮方法が変わったストリーム、位置が変わった段落、ずれた座標などです。このページでは、その仕組みと、ゴールデンを誰も読まない古い参照にせず、信頼できる状態に保つ方法を説明します。
なぜ重要なのか
「なぜ重要なのか」という見出しのセクションPDF の生成は長いパイプラインであり、知らないうちにドリフトし得る箇所が数多くあります。「何も変えていないはず」のリファクタリングが、いつの間にかオペレーターの順序を入れ替えたり、変換行列を変更したり、テーブルのセルをわずかにずらしたりすることがあります。ユニットテストでは、こうした変化をほとんど捕捉できません。確認しようと考えた値は検証しますが、想定しなかった何千ものバイトは検証しないからです。構造ベースの技法と仕様ベースの技法はそれぞれ異なる種類のエラーを検出し、一方が他方を包含することはありません(ISO/IEC/IEEE 29119-4, Annex A)。ゴールデンファイルは、1 つのアサーションではなく出力全体をピン留めする、例による仕様です。
リスクは双方向にあります。厳格すぎるゴールデンは、無害な変更のたびに失敗し、何も証明しなくなるまで盲目的に再承認されてしまいます。緩すぎるゴールデンは、本物のリグレッションを見逃してしまいます。そのバランスを正しく取ることこそが、この技術の要です。
- ゴールデンファイルとは、既知の正常なエンジン動作から生成され、リポジトリにコミットされた、ピン留めされた参照出力です。
- ゴールデンテストは出力を再生成し、ピン留めされた参照との差分を取ります。差分があればテストは失敗し、人間による判断が求められます。
- NextPDF は意味があり、かつ安定したレベルで比較します。生のバイトではなく、抽出されたテキストと正規化された構造オペレーターです。なぜなら、生のバイトにはリグレッションではないノイズ(タイムスタンプ、サブセットの順序、圧縮)が含まれるからです。
- ゴールデンの更新は、明示的な
GOLDEN_UPDATEスイッチでのみ行われる、意図的でレビュー済みの操作です。変更されたものを何でも自動的に「受け入れる」ことは決してありません。 - ゴールデンは、スナップショットテストやキャラクタリゼーションテストとは決定的な点で異なります。ゴールデンは決して自動更新されないという点です。
NextPDF のアプローチ
「NextPDF のアプローチ」という見出しのセクションエンジンのゴールデンテスト基盤は、バイト比較ではなく、明示的な2 層の差分を使用します。
- Generate Render the fixture input through the current engine.
- Layer 1 — text Extract human-readable text from the content stream; diff against the text golden. Catches dropped or reordered content and encoding regressions.
- Layer 2 — structure Extract ordered PDF operators, normalise coordinates to a fixed precision, diff against the operator golden. Catches layout shifts and broken structure.
- Decide Any diff fails the test; a human judges whether it is a regression or an intended change.
あえて比較しないものは、比較するものと同じくらい重要です。生のバイト出力は除外されます。タイムスタンプ、フォントサブセットの順序、ストリームの圧縮が、正しさへの信頼を高めることなくテストを脆くしてしまうからです。ピクセル単位の画像差分は、外部レンダラーを必要とし、環境のばらつきを取り込んでしまうため、この層からは除外されています。浮動小数点の座標は固定精度に正規化されるため、無意味な丸めのノイズがリグレッションを装うことはありません。これが、動作をテストするゴールデンと、一時的な環境ノイズをテストするゴールデンとの違いです。
この選択は、このページの再現性プロファイルにも表れています。つまり構造的(structural)です。NextPDF は 3 つのプロファイルを文書化しています。bitwise(正確なバイトが再現される)、structural(オブジェクトグラフとオペレーター列が再現され、無害なバイトレベルのばらつきは許容される)、semantic(意味が再現される)です。この層のゴールデンテストは、設計上、構造的プロファイルを検証します。だからこそ、これらの参照は圧縮ライブラリのバージョンアップには耐えても、テーブルの移動には依然として失敗するのです。
エビデンスが示すこと
「エビデンスが示すこと」という見出しのセクションEvidence: Test-backed 2 層の比較(テキスト抽出、次に正規化された構造オペレーターの比較)は、エンジン自身が文書化しているゴールデン手法であり、生のバイト、画像差分、厳密な浮動小数点の比較は、上記の理由から対象から明示的に外されています。 ゴールデンスイートは、 明示的に定義され、個別に実行可能なテストスイートであり、スナップショットスイートやキャラクタリゼーションスイートとは区別されます。
Evidence: Test-backed 誠実さを保つ仕組みは具体的です。
ゴールデン参照は手書きではなく、生成された成果物です。 参照の上書きは、
明示的な GOLDEN_UPDATE 環境スイッチの背後でゲートされており、
頻繁ではなく、常にレビューされる操作として文書化されています。 対照的に、エンジンのスナップショットテストは初回実行時に再生成され、更新フラグを通じてドリフトを受け入れます。 そして、
キャラクタリゼーションテストは、レガシーな動作が正しいと主張することなく、それを固定します。 この 3 つは、意図的に異なるツールです。
Evidence: Standard-backed ゴールデンは例による仕様です。 Spec: ISO/IEC/IEEE 29119-4, Annex A ISO/IEC/IEEE 29119-4 Annex A は、仕様ベースの技法と構造ベースの技法が異なる種類のエラーを捕捉すること、そして戦略としてそれらを組み合わせるべきであることを述べています。 だからこそ、 ゴールデンは、 テスティングピラミッドの中で、ユニットテストや構造テストの代わりではなく、それらと並んで位置するのです。
ゴールデンテストは仕組みとしては単純であり、規律が求められるのはその周囲のワークフローです。
<?php
declare(strict_types=1);
// 1. The fixture: a fixed HTML input committed next to the test.// tests/Golden/fixtures/html-inputs/002-basic-table.html
// 2. The pinned references, generated once from known-good behaviour:// 002-basic-table.text.golden (Layer 1 — extracted text)// 002-basic-table.operators.golden (Layer 2 — normalised operators)
// 3. The run compares; ANY difference fails:// vendor/bin/phpunit --testsuite Golden
// 4. An intended behaviour change is the ONLY time references move,// and it is explicit and reviewed — never automatic:// GOLDEN_UPDATE=1 vendor/bin/phpunit --testsuite Golden//// The regenerated *.golden files land in the diff of the same change// that altered behaviour, so a reviewer sees the output delta next to// the code delta and signs off on both together.この例が示しているのは、プロセスそのものです。テストコードは差分を取るだけです。ゴールデンを信頼できるものにしているのは、変更された参照が、エンジン変更と同じ変更の中で出力としてレビューされる点です。
よくある誤解
「よくある誤解」という見出しのセクション最もよくある誤りは、ゴールデンテストをバイト単位のテストとして扱うことです。NextPDF のゴールデンは、ファイルのバイトそのものではありません。抽出されたテキストと、正規化された構造オペレーターです。生のバイトを検証すると、新しい zlib のバージョン、異なるサブセットタグ、再生成されたタイムスタンプで失敗してしまいますが、いずれもリグレッションではありません。そしてそのテストは、1 週間のうちに再承認を繰り返され、役に立たなくなってしまうでしょう。(正確なバイトを本当に再現しなければならない場合、それはゴールデンではなく、別個でより厳格な bitwise 再現性プロファイルです。)
2 つ目の誤りは、ゴールデンスイートがグリーンであれば正しさが証明されると思い込むことです。証明するのは変化していないことです。バグのある出力から生成されたゴールデンは、そのバグを忠実に固定してしまいます。ゴールデンは、既知の正常なベースラインからのリグレッションを防ぎますが、そのベースラインが正しかったことを立証するものではありません。それこそが、ユニット、構造、適合性の各層の役割です。
限界と境界
「限界と境界」という見出しのセクションゴールデンテストが答える問いは、ただ 1 つです。すなわち、出力がピン留めされた参照から変化したかどうかです。参照がそもそも正しかったかどうかについては、何も語りません。また、パフォーマンス、適合性、セキュリティを測定することもありません。それらは別の層の役割です。フィクスチャコーパスのサイズ、スイートの合格率、各種カバレッジ数値は、継続的インテグレーションのアーティファクトから生成され、ビルドとともに公開される生きた品質シグナルです。それらは陳腐化するおそれがあるため、ここにはあえて記載していません。
正確なディレクトリレイアウト、コンパレーターの内部、更新スイッチは、エンジンのテスト基盤が所有しており、変化する可能性があります。この説明と食い違う場合は、テスト構成が信頼すべき根拠となります。このページは、他のライブラリのスナップショットやゴールデンのツールについて、いかなる主張も行いません。
関連ドキュメント
「関連ドキュメント」という見出しのセクション- NextPDF のテスティングピラミッド — ゴールデン層が 5 つの層の中でどこに位置し、それ自体が何を証明するか。
- ミューテーションテスト解説 — NextPDF が、ゴールデンを含む自身のテストが実際に欠陥を検出することをどう確認するか。
- あらゆる箇所で厳格な型を — ゴールデンが実行される前に、ある種の欠陥を取り除く静的解析。
- ゴールデンファイル — 既知の正常なエンジン動作から生成されてコミットされた、ピン留めされた参照出力。テストが実行のたびにこれと差分を取ります。決して自動更新されません。
- 2 層の差分 — NextPDF のゴールデン比較。生のバイトの代わりに、抽出されたテキスト(Layer 1)と正規化された構造オペレーター(Layer 2)を用います。
- スナップショットテスト — 関連はあるが別個の技法。参照が初回実行時に再生成され、ドリフトは更新フラグを通じて受け入れられます。
- キャラクタリゼーションテスト — 既存の動作が正しいと主張することなくそれを固定するテスト。通常、リファクタリングを安全にするために用いられます。
- 再現性プロファイル — 出力が再現されなければならないレベル。bitwise(正確なバイト)、structural(オブジェクトグラフとオペレーター列。無害なバイトのばらつきは許容)、semantic(意味)のいずれか。このページで扱うゴールデンテストは、構造的プロファイルを検証します。
GOLDEN_UPDATE— ゴールデン参照の上書きを許可する、明示的な環境スイッチ。まれで、レビューされる操作です。