2.4 テスト戦略
概要と動機
テスト戦略とは、チームがコードを壊さずに素早く変更できるよう、何を、どのレベルで、どれだけ自動的に、どの確度でテストするかについての意図的な選択の集合です。テストは、大きな組織が頻繁かつ安全にデプロイできるようにするものです。期待される振る舞いを符号化し、回帰を捉え、エンジニアにリファクタリングする自信を与えます。一貫した戦略がなければ、テストは二つの悪い方向のどちらかに行きがちです。存在しない(恐れに駆られた、動きの遅い開発)か、肥大する(誰も信頼しない何千もの遅く不安定なテスト)か。
大きなチームにとっては、どの単一のテストよりも戦略が重要です。共有されたコードベースで働く何百人ものエンジニアには、速く信頼できる安全網が必要です。それがなければ、あらゆる変更が危険になり、あらゆるリリースが手作業の苦行になります。テストはまた、意図された振る舞いの実行可能なドキュメントとしても働き、元の作者が去ったあとには、それは計り知れない価値を持ちます。戦略は、テストスイートがデリバリーを速める資産になるか、遅くする負債になるかを決めます。
企業や政府の文脈では、テストにはさらに重みがあります。規制は、文書化されたテストカバレッジと証拠を求めることがあります。安全が重要なシステムや市民向けのシステムは、高い保証を要求します。アクセシビリティとセキュリティのテストは、法的に求められることがあります。そのため戦略は、速度、確度、コスト、コンプライアンスの釣り合いを取らなければならず、カバレッジを、操作される目標ではなくシグナルとして扱わなければなりません。
主要原則
- 数字を達成するためではなく、変更する自信を得るためにテストします。
- 速く、信頼でき、隔離されたテストを好みます。遅いテストや不安定なテストは、スイートを有用にする信頼を蝕みます。
- 本物の確度を与える最も低いレベルまでテストを押し下げ、遅く広いテストは、本物の統合リスクのために取っておきます。
- 不安定なテストは壊れたテストです。不安定さを第一級の欠陥として扱います。
- カバレッジはシグナルであって目標ではありません。些末なコードの高いカバレッジは、ほとんど何も証明しません。
- 実装の詳細ではなく、振る舞いと契約をテストし、テストがリファクタリングを生き延びるようにします。
- 非機能テスト(アクセシビリティ、性能、セキュリティ)を、後付けではなく戦略の一部にします。
推奨事項
テストピラミッドを既定として使い、その批判も知る
多くの高速なユニットテスト、より少ない統合テスト、少数のエンドツーエンドテストを既定とします。スコープが広がるほど、コストと脆さが増すからです。批判も知っておいてください。形は教条ではなく、アーキテクチャに従うべきです。サービスの多いシステムには、より大きな統合層が必要かもしれず(「テスティングトロフィー」)、本当の目標は、特定のシルエットではなく、コストと速度の単位あたりの確度です。何をするにせよ、遅いエンドツーエンドのテストが大半を占める逆さまのピラミッドは避けてください。
役立つ所でTDD、BDD、仕様駆動開発を採用する
テスト駆動開発(TDD)を使って、設計を導き、テスト容易性を保証します。特に複雑なロジックに。それはテストの規律であると同時に、設計の規律です。振る舞い駆動開発(BDD)を使って、ステークホルダーと共有するドメインの言葉でテストを表現します。これは、規制された環境や要件の多い環境での受け入れ基準に価値があります。仕様駆動開発はさらに一歩進みます。実行可能な仕様(例として表現された、合意された振る舞い)を、実装を導き検証する唯一の信頼できる情報源として扱います。これは、政府や規制されたプログラムのように、要件が受け入れの証拠に追跡可能でなければならない所で輝きます。これら三つすべてに関連するのがシフトレフトテストです。検証をライフサイクルのできる限り早い段階に動かし、コードと並行して、あるいは前にテストを書き、継続的に実行して、遅いテストフェーズや本番ではなく、修正が最も安いときに欠陥を捉えます。これらのどれも、あらゆる所で必須ではありません。明確さを加える所に適用してください。
価値の高いコードには高度な技法を用いる
プロパティベーステストを使って、多くの生成された入力にわたる不変条件を確かめ、例ベースのテストが見逃すエッジケースを捉えます。パーサーと信頼できない入力の境界にファズテストを使って、クラッシュとセキュリティの欠陥を見つけます。ミューテーションテストを使って、テストが注入された不具合を実際に検出するかを測ります。これは、生のカバレッジよりはるかに良い品質のシグナルです。シリアライズされた出力にはスナップショットテストを控えめに使い、スナップショットを盲目的に再承認する罠に注意してください。
テストデータを管理し、合成データを使う
統制された隔離されたテストデータでテストを決定的にし、テスト同士を結合する共有の可変フィクスチャを避けます。本物の個人情報を露出せずに本番の特性を映す合成データを生成します。これは、プライバシー規則がテスト環境での本番データの使用を禁じている所では不可欠です。各テストが必要なデータを正確に構築できるよう、ファクトリーやビルダーを提供します。
不安定なテストを欠陥として扱う
不安定さを自動的に検出し、不安定なテストをブロッキングの経路から外し、期限を決めて修正または削除します。ランダムに失敗するスイートは、エンジニアに失敗を無視するよう訓練し、その価値のすべてを破壊します。不安定さの率を追跡し、信頼性をテストスイート自体の明示的な品質指標にします。
カバレッジをシグナルとして使い、非機能テストを加える
テストされていない領域を見つけるためにカバレッジを測りますが、それをアサーションのないテストによる操作を招く厳しい目標にしてはいけません。深さのためにミューテーションテストで補完します。アクセシビリティテスト(自動チェックと手動の監査)、性能テスト(負荷とレイテンシのベースラインと退行検出)、セキュリティテスト(依存関係のスキャン、静的解析、動的テスト)をパイプラインに組み込みます。
トレードオフ: 長所と短所
| テストの種類 / 実践 | 長所 | 短所 |
|---|---|---|
| ユニットテスト | 速く、正確で、安く、安定 | 統合やシステムレベルのバグを見逃す |
| 統合テスト | インターフェースや結線の欠陥を捉える | 遅い。セットアップが多い。より脆い |
| エンドツーエンドテスト | 実際の振る舞いへの最高の確度 | 遅く、不安定で、保守が高価 |
| TDD | より良い設計。テスト容易性の保証 | 学習曲線。最初は遅く感じる |
| プロパティベーステスト | エッジケースを見つけ、不変条件を符号化する | プロパティで考える必要がある。書くのが難しい |
| ミューテーションテスト | テストの有効性の真の尺度 | 計算コストが高い。実行が遅い |
| 高いカバレッジの目標 | テストされていないコードを表面化する | 操作されうる。価値の低いテストを動機づけうる |
中心的なトレードオフは、確度と、速度およびコストの間にあります。より広いテストはより多くの確度を与えますが、実行が遅く、より頻繁に壊れます。より狭いテストは速く安定していますが、システムレベルの欠陥を見逃します。適切な組み合わせは、フィードバックの秒あたり、保守の時間あたりの確度を最大化します。そして過剰なテストは本物の失敗モードです。冗長で、遅く、脆いテストの肥大したスイートは、防ぐバグよりコストがかかりえます。
チームで議論すべき問い
どの非機能テスト、アクセシビリティ、性能、セキュリティがリリースをブロックすべきで、どれは報告のみにすべきですか。 本章は、非機能テストは後付けではなく戦略に属すると論じ、アクセシビリティは法的に求められうること、セキュリティテストは運用認可の証拠の一部になりうることを指摘しています。大きなシステムや市民向けのシステムでは、ブロッキングのゲートはデリバリーを遅くしますが、本番で見つかったアクセシビリティやセキュリティの欠陥は、テストをはるかに上回る是正、評判、法的コストを伴います。それを決めるシグナルを持ち込んでください。規制上のリスク、システムが市民向けか、そしてこれらの欠陥が現在どれだけ頻繁に本番に漏れているか。法的に求められるチェックをブロッキングにし、リスクの低いチェックは傾向とともに報告のみにして、ゲートが教条ではなく本物のリスクを反映するようにします。答えは、何がマージでき、何ができないかを直接決めます。
カバレッジの割合をゲートとして厳しく設定していますか。そうなら、エンジニアがアサーションのないテストでそれを操作するのを何が止めますか。 本章は、カバレッジはシグナルであって目標ではなく、些末なコードの高いカバレッジはほとんど何も証明せず、厳しい目標は操作を招くと断言しています。大きな組織全体に課された単一の数字は、確実に、何もアサートせずにコードを実行するだけのテストを生み、指標を上げながら本物の確度を下げます。議論により良いシグナルを持ち込んでください。最も価値の高いモジュールにおけるミューテーションテストのスコアで、テストが注入された不具合を実際に検出するかを測ります。カバレッジはテストされていない領域を見つけるために、ミューテーションテストは深さのために使い、どちらもリーダーシップが単独で追跡する目標にすることに抵抗してください。数字が本当に役立つ所と、芝居を招くだけの所を決めてください。
テストスイートが遅くなりすぎて、エンジニアが待てなくなったとき、方針は何ですか。 本章の中心的なトレードオフは確度と速度およびコストであり、過剰なテストを、肥大し冗長で遅いスイートが、防ぐバグよりコストがかかる本物の失敗モードとして挙げています。大きなチームでは、スイートの実行時間は、あらゆる変更で支払う共有の税であり、人々が迂回することを学んだスイートは、その価値のすべてを失います。証拠を持ち込んでください。CIの経過時間、最も遅いテスト、冗長なエンドツーエンドのカバレッジが、より安いユニットテストとどれだけ重複しているか。本物の確度を与える最も低いレベルまでテストを押し下げ、並列化し、冗長で遅いテストを期限を決めて削除します。目標は、生のテスト数ではなく、フィードバックの秒あたりの確度の最適化です。
テストが不安定になったとき、誰がそれを所有し、どれだけ速く修正または削除しなければならず、何がその期限を徹底しますか。 本章は不安定なテストを壊れたテスト、第一級の欠陥として扱います。ランダムに失敗するスイートは、大きなチームに赤いビルドを無視するよう訓練し、全員が依存する安全網を静かに破壊するからです。相反する圧力は本物です。不安定なテストの隔離は今日のデリバリーを妨げなくしますが、本物の断続的なバグを覆い隠すリスクがあり、それでブロックすれば、純粋な雑音かもしれない失敗のために何百人ものエンジニアが止まります。決着をつける証拠を持ち込んでください。現在の不安定さの率、隔離されたテストが誰かが触れるまでどれだけ置かれるか、隔離されたテストのうち、本物の欠陥を隠していたものがいくつあったか。隔離されたすべてのテストに担当者を割り当て、修正または削除の厳しい期限を設け、信頼性をスイート自体の明示的な指標として追跡します。緑のビルドがリリースの証拠の一部になる企業や政府の設定では、管理されない隔離の山は監査上の負債でもあります。内々に信頼しないと合意したシグナルの上で出荷しているからです。
テスト環境で本番データを使うことは許されていますか。許されないなら、本物の欠陥を捉えるのに十分忠実な合成データをどう生成しますか。 本章は、プライバシー規則がしばしばテストでの本物の個人データを禁じていること、そして合成データは本番の特性を映さなければ、テストが偽りの確度を与えることを、率直に述べています。大きな組織では、緊張は忠実度とコンプライアンスの間にあります。本番データは合成データが見逃す乱雑なエッジケースを捉えますが、そのコピーのすべてが、露出と義務を増やします。具体的な点を持ち込んでください。どのデータセットが個人データや規制対象のデータを含むか、プライバシーとデータ所在地の規則が実際に何を求めているか、現在のフィクスチャが本番で見られる分布とエッジケースをどれだけよく再現しているか。各テストが必要なデータを正確に構築できるようファクトリーやビルダーを標準化し、実際の人口統計と量の分布に合う合成データの生成に投資します。政府や規制されたプログラムでは、テスト環境で市民データを使うことは近道ではなく、報告義務のある侵害なので、最初の環境を立ち上げる前に、データ戦略を決めておかなければなりません。
TDD、BDD、仕様駆動開発は、どこで任意ではなく期待され、誰が決めますか。 本章はこれらを、明確さを加える所に適用する規律として示しており、あらゆるコードの行への義務づけではありませんが、大きなチームは、実践がチームごとに分断しないよう、共有の既定から恩恵を受けます。トレードオフは、設計とトレーサビリティの利点(政策の専門家がレビューできる実行可能な仕様、リファクタリングを生き延びるテスト)と、一律の義務づけを裏目に出させる本物の学習曲線と前もっての遅さの間にあります。範囲を決める証拠を持ち込んでください。複雑なロジックや高い変更失敗率を持つモジュールはどれか、受け入れ基準が要件に追跡可能でなければならない所はどこか、すでにこれらを実践しているチームが速度と欠陥率についてどう報告しているか。期待は複雑なロジックと要件の多い領域のために取っておき、単純なコードは自分で選ばせます。ソフトウェアが実装する法律に追跡可能でなければならない規制されたプログラムや政府のプログラムでは、実行可能な受け入れの証拠を伴う仕様駆動開発は、好みというより運用認可への道なので、どこで必要かを明示的に名指ししてください。
セクター別の視点
スタートアップ。 小さなチームにはQAを配置できないので、スイートに稼がせてください。すべてのコミットでの高速なユニットテストと、生計を立てる一つの経路についての数本のエンドツーエンドテスト、そして保守しないものは何もなし。カバレッジの目標は省き、最も壊したくないロジックをテストして、手作業の回帰テストなしに一日に何度も出荷できるようにします。不安定なテストはその日のうちに直してください。この段階では、チームが無視することを学んだスイートは、スイートがないよりも悪いからです。
小規模事業者。 専任のテストエンジニアはおらず予算も厳しいので、サポートできない特注のハーネスではなく、すでに動かしているフレームワークやツールに組み込まれたテストに頼ってください。収益と顧客の信頼を守る少数のチェックを優先し、ホスト型のCIを使って、ビルドインフラを自分で保守しないようにします。アクセシビリティとセキュリティのスキャンは、作るよりサービスとして買うことを好みます。一つの見逃した欠陥が、ツールの一年分より高くつきうるからです。
大企業。 多くのチームにわたって、戦略の問題は一貫性です。共有のピラミッドの既定、自動の不安定テストの隔離、どこでも同じ意味を持つ非機能のゲート。そうすれば、誰が作ったかにかかわらず、緑のビルドが信頼できます。スイートの実行時間を共有の税として予算化し、積極的に並列化してください。CIの経過時間は、すべてのエンジニアによって、すべての変更で支払われるからです。カバレッジとミューテーションのスコアは、リーダーシップが単独で追跡する数字ではなく、明確な担当者を伴うポートフォリオのシグナルとして管理します。
政府。 調達と監督は、テストを単なるエンジニアリングの衛生ではなく、証拠にします。適格性と政策のルールを、ドメインの専門家がレビューする実行可能な仕様として表現し、ソフトウェアが実装する法律に追跡できるようにし、アクセシビリティとセキュリティのテストは、法的に必要であり運用認可の証拠の一部なので、ブロッキングにします。実際の分布に合うよう生成された合成データを使ってください。テスト環境での市民データは報告義務のある侵害だからです。そしてテストの成果物を監査可能に保ち、外部のレビュアーが何が検証されたかを正確に確認できるようにします。
事例
スタートアップ。 5人のスタートアップはQAチームを持てないので、すべてのコミットで実行される高速なユニットテストのスイートと、生計を立てるサインアップからチェックアウトまでの経路をカバーする数本のエンドツーエンドテストに頼ります。創業者たちは網羅的なカバレッジを省き、代わりに最も壊したくないロジックをテストするので、手作業の回帰テストなしに一日に何度も出荷できます。不安定なテストがランダムに失敗し始めたとき、彼らはその日のうちに直します。信頼がすべての段階では、チームが無視することを学んだスイートは、スイートがないよりも悪いからです。
大企業。 大手のeコマースプラットフォームは、すべてのコミットで数分で実行される数千の高速なユニットテスト、決済と在庫の境界に絞った統合テストの集合、そして重要なチェックアウトの経路についての小さなエンドツーエンドのスイートを維持しています。不安定なエンドツーエンドのテストは自動的に隔離され、修復のために割り当てられます。エンジニアはスイートを信頼しているので、赤いビルドは本物の問題を意味すると確信して、一日に何度もデプロイします。
政府。 規制の監督のもとで運用される国の給付システムは、BDDを使って適格性のルールを、政策の専門家がレビューする実行可能な仕様として表現し、ソフトウェアが法律を実装している追跡可能な証拠を得ます。プライバシー規則がテスト環境での市民データを禁じているため、実際の人口統計の分布に合うよう生成された合成データを使います。サービスはすべての市民が使えなければならないので、アクセシビリティテストは必須でリリースをブロックします。そしてセキュリティテストは、運用認可(ATO)、つまり本番でシステムを動かす正式な承認の証拠の一部です。
ビジネスケース: 動機、ROI、TCO
テストの見返りは、ソフトウェアを素早く安全に変更できることであり、それは持続的なデリバリー速度の基礎です。信頼できる自動化されたスイートは、遅くて高価な手作業の回帰テストに取って代わり、欠陥を、本番ではなくリリース前の、修正が最も安いときに捉えます。規制された、あるいは市民向けのシステムでは、本番の欠陥のコスト(是正、評判、潜在的な法的リスク)は、それを捉えたであろうテストのコストをはるかに上回ります。
採用のコストは本物です。テストを書いて保守し、継続的インテグレーション(CI)のインフラを構築します。しかしテストしないコストはより高く、複利で増えます。遅々として進まなくなる恐れに駆られた開発、頻繁な回帰、スケールできない手作業のリリースプロセス。過剰なテストにもコストがあるので、論拠は最大数のテストではなく、よく設計された戦略のためのものです。リーダーシップに論拠を示すには、スイートをデプロイ頻度、変更失敗率、平均復旧時間に結びつけ、それが置き換える手作業のテストの労力と、防ぐ本番のインシデントを定量化してください。
アンチパターンと落とし穴
- アイスクリームコーンのテスト: 薄いユニットの土台の上の、大半が遅いエンドツーエンドのテスト。遅く、不安定で、高価。
- 目標としてのカバレッジ: 何も証明しない、アサーションのないあるいは些末なテストで、割合を追うこと。
- 実装の詳細のテスト: 内部に結合し、リファクタリングのたびに壊れて変更を妨げるテスト。
- 許容された不安定さ: チームに赤いビルドを無視するよう訓練するランダムな失敗。
- 共有された可変のテストデータ: 互いに干渉し、予測できない形で失敗するテスト。
- テストでの本番データの使用: 起こるのを待っているプライバシーとコンプライアンスの侵害。
- 省かれる非機能テスト: 本番でしか発見されないアクセシビリティ、性能、セキュリティ。
- 信頼されないスイート: エンジニアが日常的に再実行したり迂回したりするほど信頼できず、その目的を無効にしていること。
成熟度モデル
- レベル1、開始: テストは手作業で反応的です。自動のカバレッジは最小限で、回帰は頻繁で遅れて捉えられ、しばしばスイートではなくユーザーによって捉えられます。
- レベル2、発展: 自動のユニットテストと一部の統合テストはありますが、スイートは遅いか不安定で、信頼は低く、実践はチームごとに大きく異なります。
- レベル3、標準化: バランスの取れた、速く信頼できるスイートがすべての変更をゲートします。文書化されたピラミッドの既定、不安定テストの方針、非機能テスト(アクセシビリティ、性能、セキュリティ)が、チームを通じて一貫して徹底されます。
- レベル4、管理: スイートの健全性がベースラインに対して測定され、制御されます。不安定さの率、CIの経過時間、価値の高いモジュールのミューテーションスコア、流出した欠陥の率が追跡されレビューされ、カバレッジは複数のシグナルの一つで、ゲートは意見ではなく証拠で作動します。
- レベル5、オーケストレーション: 高度な技法(プロパティベース、ミューテーション、ファズ)が価値の高いコードを対象とし、テストはデプロイ頻度、変更失敗率、平均復旧時間のようなデリバリー指標と統合されています。組織はスイートをアーキテクチャとリスクに合わせて継続的に作り直し、冗長なテストを退役させ、証拠が欠陥の漏れを示す所に投資します。
議論のためのアイデア
- テストの分布は実際にどんな形で、アーキテクチャとリスクに合っていますか。
- コードが、例ベースのテストではなく、プロパティベースやミューテーションのテストに値するかをどう判断しますか。
- 不安定なテストの方針は何で、実際に徹底されていますか。
- 機微な情報を漏らさずに、現実的な合成データをどう生成しますか。
- カバレッジが本当に役立つのはどこで、操作されたのはどこですか。
- AIが生成したテストは、雑音ではなく確度を加えるよう、どうレビューされるべきですか。
要点
- 変更する自信を得るためにテストします。速度とコストの単位あたりの確度を最適化します。
- ピラミッドを既定として使いますが、テストをアーキテクチャに合わせて形づくります。
- 不安定なテストを欠陥として、カバレッジを目標ではなくシグナルとして扱います。
- 価値がコストを正当化する所に高度な技法を適用します。
- アクセシビリティ、性能、セキュリティのテストを戦略に含め、プライバシーを守るために合成データを使います。
参考文献とさらなる読み物
- Kent Beck, Test-Driven Development: By Example
- Lisa Crispin and Janet Gregory, Agile Testing: A Practical Guide for Testers and Agile Teams
- Gerard Meszaros, xUnit Test Patterns: Refactoring Test Code
- Michael Feathers, Working Effectively with Legacy Code
- Nicole Forsgren, Jez Humble, Gene Kim, Accelerate: The Science of Lean Software and DevOps
- Martin Fowler, articles on the Test Pyramid and test-related patterns