コンテンツにスキップ

NextPDFのテストピラミッド

Spec: ISO/IEC/IEEE 29119-4 Spec: ISO/IEC 25010 Evidence: Test-backed PHPStan: Level 10

NextPDFには1種類のテストだけがあるわけではありません。テストには5つの階層があり、それぞれがエンジンに関する異なる問いに答えます。なぜなら、PDFはユニットテストに合格していても、ディスク上では構造的に壊れたファイルである場合があるからです。このページでは、5つの階層と、各階層が何を証明する責任を担うのかを示します。

PDFエンジンは、異例なほど広い障害領域を持っています。同じコードパスが、関数としてもバイト列としても正しいにもかかわらず、準拠リーダーに拒否されるファイルを生成してしまうことがあります。また、改ページの箇所でのみ微妙に誤ってレンダリングされるファイルを生成することもあります。エンジンを単一の粒度でテストすれば、得られる確信はその粒度についてだけであり、それ以外については何も得られません。

標準規格はこの点を明確にしています。仕様ベースのテスト設計技法と構造ベースのテスト設計技法は互いに関連しておらず、テスト戦略では複数の基準を用い、少なくとも機能的な基準を1つ、構造的な基準を1つ含めることが推奨されています(ISO/IEC/IEEE 29119-4, Annex A)。単一の階層は、優れた戦略を小さくしたものではありません。それだけでは別個の戦略であり、しかも不完全です。

NextPDFのテストは、底辺から頂点まで5つの階層に整理されています。

  1. ユニット — 1つのクラスまたは関数を分離して検証。幅広い底辺。
  2. 統合 — モジュール境界をまたいで連携するユニット。
  3. 構造 — 出力されたPDFのオブジェクトグラフ、相互参照テーブル、トレーラーが整形式であり、準拠していること。
  4. ビジュアル — レンダリングされたページが、規定の許容範囲内で承認済みのリファレンスと一致すること。
  5. ゴールデン — 最終出力の意図しないドリフトを検出する、ピン留めされたエンドツーエンドのフィクスチャ。頂点。

各階層は、その下の階層が証明できないことを証明します。どれも飾りではありません。ピラミッドの形が表すのは です。安価なユニットテストは多く、高価なエンドツーエンドテストは少ないということであって、重要度ではありません。

これらの階層は実体のあるものであり、努力目標ではありません。リポジトリのPHPUnit設定は、それぞれをディレクトリと1対1に対応付けた名前付きテストスイートとして宣言しています。したがって階層は、テストランナーの実行対象であって、スライド上のラベルではありません。シニアエンジニアなら見覚えのあるスイートとして、UnitIntegrationGoldenSnapshotReproducibilityConformanceStandards、そして Performance があり、それぞれが独自の実行プロファイル(分離、時間バケット、継続的インテグレーションでデフォルト実行されるかどうか)を持っています。

この分離は意図的なものです。底辺にある高速な階層(Unit)は、テストあたり1秒という予算で、変更のたびに実行されます。より遅く、環境に左右される階層 — ビジュアルレンダリング、完全な準拠性、パフォーマンス — はオプトインまたは夜間実行にしています。これにより、より深いチェックを犠牲にせず、日常のパスを高速かつ決定的に保てます。厳格な型付けがスタック全体を支えています。エンジンは Spec: PHPStan, Level 10 で、エラー予算をゼロに固定して解析しているため、大きなカテゴリの欠陥はそもそもテストに到達することがありません。

  1. Tier 1 of 5 Unit Isolated behaviour of a single class or function; the broad base.
  2. Tier 2 of 5 Integration Collaborating units across a module boundary.
  3. Tier 3 of 5 Structural The emitted PDF object/xref structure is well-formed and conformant.
  4. Tier 4 of 5 Visual Rendered output matches an approved reference within tolerance.
  5. Tier 5 of 5 Golden End-to-end byte/lossless fixtures pinned as the contract; the apex.
NextPDF の 5 つのテスト階層、底辺から頂点まで。ユニットは幅広く高速な底辺で、その上の各階層は下の階層が証明できない特性を証明し、頂点のゴールデンなエンドツーエンドのフィクスチャに至ります。幅は量のヒントにすぎず — 重要度を順位づけるものではありません。

Evidence: Test-backed これら5つのスイートは、エンジン設定で宣言された PHPUnit テストスイートとして実在し、それぞれが独自のディレクトリと実行プロファイルに結びついています。このページで使う階層の語彙は、テストインフラが使う語彙と一致しています。

Evidence: Standard-backed 複数の 階層が必要な理由は、 Spec: ISO/IEC/IEEE 29119-4, Annex A に根拠があります。 カバレッジ基準は、すべてが相互に関連しているとは限らず、戦略では機能的技法と構造的技法を組み合わせることが推奨されています。重要なのは、同じ附属書が、 カバレッジ基準間の包摂順序は、それらが欠陥を表面化させる能力、つまり テスト有効性 を何ら示すものではないと指摘している点です (ISO/IEC/IEEE 29119-4, §C.2.4)。「カバレッジを増やす」ことは、「より良いテスト」と同じ主張ではありません。

Evidence: Standard-backed どの 特性を証明するかの選択は、 Spec: ISO/IEC 25010 の製品品質特性に対応します。すなわち、機能的正しさ(ユニット、統合)と、 PDFを後段で実際に利用できるようにするファイルレベルの特性 (構造、ビジュアル、ゴールデン)です。品質モデルは、利用状況によって重要になる特性が異なることを明示しています。

これらの階層は、エンジン自体のスクリプトから指定できます。単一のフォーマッターへの変更は、底辺で検証されます。ドキュメントファサードへの変更は、複数の階層をまたいで検証されます。

<?php
declare(strict_types=1);
// Tier 1 — Unit: one unit, isolated, fast.
// composer test:unit → phpunit --testsuite Unit
// Tier 2 — Integration: collaborating units across a boundary.
// composer test:integration → phpunit --testsuite Integration
// Tier 3 — Structural: the emitted PDF object graph is well-formed.
// vendor/bin/phpunit --testsuite Conformance
// Tier 4/5 — Visual + Golden: rendered/serialized output vs a pinned
// reference (golden is byte/structure-pinned, never auto-updated).
// vendor/bin/phpunit --testsuite Golden
// A change to the document facade touches every API, so the routing
// guidance escalates it from "unit only" to the full unit + integration
// surface — the tier you run is a function of blast radius, not habit.

この例の要点は、コマンドではなくルーティングのロジックです。どの階層を実行するかは、その変更が何を壊しうるかによって選ばれます。このインフラは、各階層を第一級の個別実行可能なターゲットにしています。

ピラミッドはしばしば 序列 として読まれます。ユニットテストは最も重要でないから底辺に、エンドツーエンドテストは最も重要だから頂点に(あるいはその逆)、という読み方です。どちらでもありません。縦軸が示すのは、おおむねコストと量です。高速で安価なユニットテストが多数あって幅広い底辺を形成し、その上ほど、より少なく、より遅く、より忠実度が高いテストになります。ゴールデンテストは、ユニットテストより「優れている」わけではありません。ゴールデンテストは異なる障害を、より後段で、より高いコストをかけて捉えるものであり、その下にある数千の高速なチェックの代わりにはなりません。

2つ目の誤解は、カバレッジの数値が高ければピラミッドが健全だ、というものです。そうではありません。カバレッジが測るのは実行であって、検出ではありません。標準規格は、カバレッジの順序を欠陥発見能力と同一視することを明確に否定しています。そのギャップを表面化させるために、ミューテーションテスト が存在します。

このページが説明するのは戦略の 形と意図 であって、現在の結果ではありません。テスト件数、カバレッジの割合、ミューテーションスコアは、ここでは意図的に記載していません。これらは継続的インテグレーションの成果物から生成される、変化し続ける品質シグナルです。現在の数値はビルドとともに公開されます。本文に固定してしまえば、いつの間にか古びてしまいます。記載されている唯一の数値 — PHPStan Level 10 — は、エンジンの静的解析設定で検証可能な安定した設定上の事実であって、測定値ではありません。

階層の 名称 は、安定したアーキテクチャの語彙です。スイートの正確な構成とその実行プロファイルはエンジンとともに進化し、テスト設定によって管理されます。この説明と食い違う場合は、テスト設定が正となります。このページは特定の合格率を主張せず、他のいかなるライブラリのテスト戦略とも比較しません。

  • テスト階層 — 1種類の特性(たとえばユニットの振る舞いや構造的妥当性)を証明する戦略のレベル。NextPDFは5つ使用します。
  • 構造テスト — 単に戻り値を確認するのではなく、出力されたPDFのオブジェクトグラフ、相互参照テーブル、トレーラーが整形式であり、準拠しているかを確認するチェック。
  • ビジュアルテスト — レンダリングされたページが、規定の許容範囲内で承認済みのリファレンス画像と一致するかを確認するチェック。
  • ゴールデンテスト — 自動更新されない、ピン留めされたリファレンス出力に対するエンドツーエンドのチェック。「出力が変わっていない」ことを保証する契約。
  • テスト有効性 — テストセットが欠陥を表面化させる能力。ISO/IEC/IEEE 29119-4 はこれをカバレッジと区別しています。頭字語に関する注記: MSI (Mutation Score Indicator) は ミューテーションテスト のページで定義されています。
  • PHPStan Level 10 — 最も厳格な静的解析レベル。NextPDFはエラー予算をゼロに固定して実行します。