見出し画像

テストケースを自動化するべきでない理由

ユーザーストーリーや受け入れ基準を基に作成したテストケースを直接自動化するというアプローチは、いかに扱いにくく、肥大化した自動テストスイートにつながるのでしょうか。


原文:Don’t Automate Test Cases
作者:Blake Norrish
日本語訳:Gen Kudo
校正者:Mari Mizuki

テストケースをテスト自動化のためのバックログとして使用することは一般的なプラクティスです。QAチームは、通常のテストの一部として、ユーザーストーリーからテストケースを作成し、その後それらのテストを自動化します。イテレーションごとに、より多くのストーリーがテストされ、またより多くのテストケースが自動化されることで、自動化されたテストスイートは拡大していきます。開発組織のリーダーは、「自動化されたテストケースの割合」といった指標を重視し、高い数値を達成できたときはチームを賞賛します。チームによっては、テストケースを自動化することを役割とする「自動化エンジニア」を採用しているところすらあります。

残念なことに、「テストケースを自動化すること」と「自動化されたテストケースの割合」を重視することは品質エンジニアリングにおけるアンチパターンであり、必然的に、ほとんど価値を生まない、肥大化し、保守が難しいテストスイートに繋がります。テスト自動化はアジャイルデリバリーにおいて重要ですが、このような過度に単純化された「機械的なテスト自動化」の考え方は、テスト自動化に対する健全なアプローチではありません。

この記事では、なぜ「機械的なテスト自動化」がよくないのかを示し、自動テストを維持し、デリバリー速度を加速するテスト自動化を行うためのより良いアプローチについて解説していきます。

テスト自動化のコストと効果

既存のテストケースを自動化することがなぜそんなに問題なのかを理解するためには、一歩下がって自動化理論を少し見直す必要があります。具体的には、テスト自動化にかかるコストとそこから得られる効果を掘り下げ、自動化されたテストの時間経過に伴う期待値を調べ、異なる種類のテストによって期待値がどのように変化するかを見ていく必要があります。それにより、機械的なテスト自動化のアプローチが、全体的なテストスイートにどのような影響を与えるかを知ることができます。

すべての自動テストには、開発のための初期コストと継続的なメンテナンスコストという2つの種類のコストがあります。当然のことながら、テストは何らかの利益をもたらすことが期待されており、それはテストを手動で実行する時間と自動テスト実行にかかる時間との差です。他にもさまざまな利点(楽しい、貴重なスキルを学べる等)がありますが、この記事ではそれらの点は考慮しないこととします。

この図は、テスト自動化をかなり単純化したものですが、この記事の目的の重要な側面を捉えています。あらゆる種類の自動テストにはコストと利益が存在し、その両方が重要です。私たちはテスト自動化の専門家として、利益を最大化し、コストを最小限に抑えることを目指しています。

テストのコストに影響するものをいくつか挙げてみましょう。

  • テストを追加するテストフレームワークの有無

  • 既存のテスト・フレームワークとテスト・スイートのクリーンさ

  • テスト状態(テストデータなど)のセットアップの可能性と容易性

  • 受け入れ可能なテストオラクルの有無

  • テストが接しているインターフェースや機能の変わりやすさ

  • テストを実行する環境の安定性

  • 自動テストの作成とメンテナンスに必要とされるQAの技術スキル

以下は、自動テストから得られる利益の大きさに影響を与える可能性のある要素の一部です。

  • テストの実行頻度(コミット毎、毎日、リリース毎など)

  • テストがテスト対象のシステムに対して有効である期間

  • 同じテストを手作業で検証すると、(時間的に、あるいはそれ以外の観点で)どれだけコストがかかるか

  • そのテストを手動で実行した場合、どの程度エラーが発生しやすいか

これらのリストはいずれも不完全ですし、経験豊富な品質エンジニアなら「でも、xyzについてはどうなの?」と聞きたくなると思います。幸いなことに、すべてのテストには期待されるコストと利益の両方に貢献する多くの要因があることを示すために、網羅的なリストは必要ありません。

自動化のコストは、初期開発コストと継続的なメンテナンスコストに分けられることは既に説明しました。自動化されたテストから得られる価値はすぐに実現されるものではなく、時間の経過とともに蓄積されていくものです。つまり、価値の全容は、作成された時点で確定するのではなく、時間の経過とともに変化するものなのです。

一般的な自動テストの価値の時間経過をグラフにすると、以下のようになります。

このグラフは、最初は純損失を示すテストの価値を表しています。つまり、初期の開発コストが節約された時間による利益を上回っているということです。しかし、時間が経つにつれて、節約された時間が初期コストと継続的なメンテナンスコストを上回り、最終的にはプラスの価値をもたらします。すべてのテストは、上記の変数に基づいてこのようなライフサイクルを辿ります。

初期開発コスト、メンテナンスコスト、そして得られる利益によっては、テストが損益分岐点に達し、より早くプラスの価値を提供する場合もあれば、損益分岐点に達しない場合もあります。

上のグラフは、最初は何らかの価値を提供していた(初期の開発後、線は上向きになっている)テストが、後に価値を提供しなくなったことを示しています。おそらく、このテストは実行されなくなったか、あまりに不安定で誰も信用しなくなったか、あるいはテスト対象の機能が廃止されたのかもしれません。理由はどうであれ、このグラフはそもそもこのテストを自動化しないほうがよかったことを示唆しています。

これが重要なポイントです。自動テストの価値は多くの変数に依存していて、自動テストが全体的な時間の節約という点で、プラスの生涯価値をもたらすこともあれば、逆にマイナスの価値をもたらすこともあるということです。

自動テストの種類

テストには様々な種類があります。ここでは、時間の経過とともにその価値がどのように変化するかを考えてみましょう。

テストには、コードレベルの小さな単体テストや、他のメソッドに依存する少し大きめの「ソーシャル」単体テスト(テストダブルを使わない(=依存関係を分離しない)タイプの単体テスト)などがあります。また、さらに大きなコンポーネントテスト、より高いレベルの統合テスト、UIをバイパスしてAPIを直接叩くテスト、APIをモックしてUIだけを動かすテスト、技術スタック全体にまたがるE2Eテストなど、多岐にわたります。

複雑でモダンなソフトウェアでは、自動テストの種類は非常に多岐にわたります。システムをどのように分解し、どのように分離するかには無限の方法があるため、それに応じて様々なテストの種類が存在します。

非自明なアーキテクチャに対するあらゆる種類の自動テストを調査することはこの記事の範囲を超えますが、入門書として『The Practical Test Pyramid』と『Testing Strategies in a Microservice Architecture』をおすすめします。ここでは、議論を簡潔に保つために、小規模な単体テストと大規模なエンドツーエンド(E2E)テストの二つに焦点を当てます。

単体テストの費用対効果の方程式と、時間経過に伴う価値のグラフはどのようになるでしょうか?単体テストの特徴には以下のようなものがあります。

  • 通常、数秒とは言わないまでも数分で書くことができる。

  • 外部の状態の影響を受けない(はずである)。つまり、モック、テストダブル、スタブなどを設定して、実行パスを決定論的に制御できる。

  • 各テストはミリ秒単位で、テストスイートは秒単位で実行できる。

  • コミットごとにCI/CDパイプライン内で実行されるだけでなく、コードを書くときに各開発者がローカルで実行することもあり、1日に何千回も実行される可能性がある。

  • 単体テストのカバレッジが100%であっても、それぞれのテストではごく小さな(たいていはたった1つの)部分を検証しているに過ぎず、アプリケーション全体として期待通りに動作していることを証明することはできない。

この評価を踏まえると、単体テストの時間経過に伴う価値のグラフは、一般的なグラフとは大きく異なる可能性があります。初期コストはほとんどかからず、メンテナンスも最小限でよく、絶えず実行されるものの、各実行で得られる価値はごくわずかです。

グラフはおそらく次のようになります。

すべての自動テストの中で最大のものであるE2Eについてはどうでしょうか?E2Eの時間経過に伴う価値のグラフはどのようになるでしょうか?

以下はE2Eテストに関するいくつかの重要なポイントです。

  • (定義上)最も多くの状態に影響されるため、最も多くのセットアップとテストデータの管理を必要とする。

  • 完全な環境に対して実行される。多くの場合、この環境の一部は共有されている。

  • 多くの(何十、何百もの)連続的な手順が含まれることが多い。

  • すべてのテストの中で最も時間がかかり、場合によっては数分間をかけて実行される。

  • 通常、ユーザー・インターフェースを介して機能を実行しなければならない。

  • 通常、CI/CDパイプラインの後半のステップで実行される。

  • 顧客が使用するようにアプリケーションが動作することを実証する唯一の種類の自動テストである。

これらの点を考慮すると、時間経過に伴う価値のグラフは下記のようになります。

このグラフは、初期費用が非常に高く、最初は純価値が大きくマイナスになることを示しています。しかし、時間をかけてこのテストを実行し続けることで、最終的には収支が均衡し、その後プラスに転じることになります。

繰り返しになりますが、このテストが最終的にプラスの価値を提供するかどうかは、このセクションの冒頭での仮定に基づいています。例えば、テストがどのくらいの期間使用されるのか、どのくらいの頻度で実行されるのか、テストの結果にどの程度の信頼性があるのか、UIなどの基本的なインターフェースが変更された場合にテストがどの程度変更されなければならないのか、実行環境がどの程度安定しているのか、などです。決して損益分岐点に到達することが保証されているわけではありません。

どこで自動化するか

E2Eテストの性質上、作成やメンテナンスに多くのコストがかかります。また、E2Eテストは必然的に多くのシステムの状態に依存しています(あるいは影響を受ける可能性があります)。システムのタイミング、同期、ネットワーク、外部依存関係の問題などの影響を受けやすいです。通常、一部またはすべての機能をウェブブラウザー、つまりソフトウェアではなく人間が使うように設計されたインターフェイスを通して動かします。これらのテストは完全な環境で実行されるため、環境の一部または全部を他のテストやユーザーと共有しなければならない可能性が高く、同時に使うことで予期せぬ結果につながる可能性があります。

これらの理由(実際には他にも多くの理由があります)により、E2Eテストは最も不確実なテストとなっています。そして、E2Eテストを書くということは、「現実に即した、完全に統合されたシステムが連携して動作する様子を示す唯一の手段である」という大きな利益によってのみ正当化されます。

他にも検討すべき多くのテストの種類があり、特に複雑なシステムにおいては、低レベルのテストの方が、高レベルで大規模なテストに比べてコストを抑えつつ、問題となっている機能をより直接的にテストできることが多いです。

言い換えれば、ユニットテストで検証できるロジックの一部を検証するために、デプロイされたサービスインスタンスに対してAPIテストを構築するべきではないということです。単一のAPIを直接呼び出すことで検証できるロジックに対してE2Eテストを構築しないなど、何かが動作していることを実証するために必要以上のシステムを持ち込むべきではありません。テストする必要のあるロジックや振る舞いを特定し、その振る舞いを正確に分離するテストを作成しましょう。より高レベルのテストは、その中のロジックではなく、実際の統合をテストするためだけに使いましょう。

包括的で重要なポイントは、E2Eテストは不確実性が高く、特定の機能をテストするのに最適な自動テストの種類であることは稀だということです。時間経過に伴う価値のグラフの観点から考えると、最小のコストで早く最大の価値をもたらすテストの種類を常に選択し、最終的な時間節約効果を過度に楽観的に見積もった大規模な自動化の取り組みには慎重になるべきです。

このような費用対効果分析は、まさに何年も前に生み出された「自動テストピラミッド(Automated Test Pyramid)」という概念に基づいています。このピラミッドでは、すべての条件が同じであれば、一般的に、小さくて速く安いテストを多くし、大きくて遅く費用がかかるテストは少なくすることを提唱しています。

テストの種類は多くあり、各テストタイプに使われる実際の名称は組織によって大きく異なる。

すべてのテストスイートが常に、そして正確にピラミッド型であるべきだとは言いませんが(Kent Doddsはトロフィー型が良いと言い、James BachはRound Earthモデルを好み、Justin Searlsはこれらの考えをただの “distraction” と言っています。)、すべてのテスト自動化には不確実性が伴うこと、そしてテストが適切なレベルで作成されていることを確認することが、この不確実性を軽減し、コントロールするのに役立つことを理解していただけたと思います。自動化担当者の仕事の大部分は、(他のチームメンバーと協力して)どの種類のテストが適切で、生涯を通じてプラスの価値をもたらす可能性が最も高いかを判断することです。

別の言い方をすれば、すべての自動化は投資、特にリスクの高い投資として扱われるべきです。自動化された様々な種類のテストは、それぞれ異なるタイプのリスクを持っています。それぞれのテストの費用と利益を継続的に評価することで、全体のリスクを管理し、投資価値を最大化する必要があります。

継続的なテスト自動化とトップ・ヘビー・スイート

機械的なテスト自動化の話に戻りましょう!

品質保証のプロがユーザーストーリーや受け入れ基準から作成するテストケースを考えた場合、これらのテストケースはどのような種類の自動テストに自然にマッピングされると思いますか?テストケースをただやみくもに自動化すると、一般的にどのような種類のテストが作成されるでしょうか?

一般的に受け入れられているアジャイルなドキュメント手法では、要件は、より高いレベルの、ユーザー中心の言語(よくユーザーストーリーとも呼ばれる)でテスターに伝えられます。 Given-When-Then 形式で記述されたストーリーと、「[ユーザー/顧客]として、[達成したいゴール]をしたい、なぜなら[理由]だから」という受け入れ基準を考えてみましょう。

ストーリーがホリゾンタルスライスされ、特定のコンポーネント(例えばRESTサービスのAPI)の動作を記述する場合でも、要件はそのコンポーネントの「ユーザー言語」で伝えられます。したがって、この文書から作成されたテストケースは、より大規模でリスクの高い種類の自動テストに自然にマッピングされます。

これが機械的なテスト自動化アプローチの根本的な問題です。テストケースを自動化することは、必然的に大規模で遅く、コストがかかるテストに過度に重きを置くことになります。なぜなら、テストケースはテスターが普段マニュアルテスト実施時に使っている言語で書かれることが一般的だからです。結果として、テストケースは時間価値分析が私たちに避けるように指示したタイプのテストにマッピングされてしまいます。

すべての新機能に対して開発ユニットテストと新しいE2Eテストが作成されることで、最上位のテストが多く、メンテナンスが大変なテストスイートに繋がります。

自動化担当者が小規模なテストよりも大規模なE2Eテストを誤って好む大きな要因として、技術的なバックグラウンドがない人々(一部の技術的バックグラウンドがある人も含まれる場合もありますが)にとってテストケースが自動化されていることを知ると心理的な安心感につながるということがあります。例えば、ビジネスリーダーは、馴染みのある言語でアプリケーションの機能を記述したテストケースを理解することができます。そして、それらが自動的にチェックされていると知ることで、深夜に怒った顧客からの電話がないと安心することができます。開発チームが90%以上のユニットテストカバレッジを持っていることを知っても、彼らにとって安心材料としては弱いものになります。

このように、テストケースの自動化率や自動化数といったメトリクスを重視する人がいるのは、それがテストの状態に対する安心感を与えるからであって、実際にシステムの振る舞いを自動的にチェックするためのより効果的で効率的な方法だからではありません。すべてのテストケースを自動化することは、安心感を与えるかもしれませんが、健全な自動化スイートを作成することにはつながりません。

機械的なテスト自動化の症状

test-cases-in、automated-test-cases-out という、機械的なテスト自動化のアプローチを使うと、非常に問題のある、しかし残念ながらよくある以下のような症状を引き起こす傾向があります。

  • 実行に何時間もかかるテスト・スイートや、夜間にしか実行できないテスト・スイート。

  • 主な任務が前回の実行における失敗の調査とトリアージで、この作業に大半の時間を使っている自動化チーム。

  • 対処法が単にテストを何度も実行するだけであるテストの失敗。

  • CI/CDパイプラインからのスイートの削除、またはノンブロッキングステップへの降格。

  • 結果が信頼できないために開発者から実行を避けられたり、あるいは完全に拒否されたりしているテストスイート。

  • 何百ものフォルダ(あるいは異なるリポジトリ!)に分散し、重複したテスト、コメントされたテスト、何をするのか、なぜそこにあるのか誰も知らないテストが何千件とあるテストスイート。

  • 自動化エンジニアが、膨大なテストスイートを管理したり、複雑さをGherkin(例:Cucumberなど)のレイヤーの後ろに隠したりするために多大な努力をしている。

これらの症状はすべて、テストスイートがチームに価値を提供していないことの兆候です。これは、残念ながら開発組織内でよく見られる問題です。苦しんでいるチームが開発プロセスの中でテスト自動化に重きを置いていることは称賛に値しますが、単純で機械的なテスト自動化の考え方で取り組んでしまっています。

健全な自動化

では、膨大なテストスイートを避けるために、どのようにテスト自動化に取り組めばよいのでしょうか?

新機能をカバーする自動化の必要性は、あらゆる種類のテストにまたがる自動化オプションを総合的に見て検討すべきです。新しい機能が必ずしも新しい最上位レベルの自動E2Eテストを必要とするわけではないことを念頭に置いてください。その代わりに、その機能をどのように最も効果的かつ効率的にカバーできるかを、すべての種類のテストを総合的に考慮して評価してください。

自動化したいのはテストケースではなく、機能です。機能性はテストケースによって部分的に記述することができますが、すべてのテストケースをテスターが手動で実行するレベルで自動化することは、決して効果的でも効率的でもありません。

実際には、新しい機能をテストする最も効果的な方法は、既存の自動テストを更新するか、テストをより適切なレベルに移動するか、あるいはまったく新しい種類のテストを作成することかもしれません。システム機能の変更に伴い、テストの追加を検討するだけでなく、テストを削除することも視野に入れるべきであることを忘れないでください。

新しい機能には新しいE2Eテストが必要だと決めつけず、あらゆる種類の自動化を検討する。

必要なテストの種類は、システムアーキテクチャ、許容できるリスクプロファイル、既存のツールなどに大きく依存します。自動化の変更に対する一般的に健全なアプローチは、次のようなものです。

  1. 可能な限り低いレベルで新しいテストを追加する。

  2. 新しい機能をカバーするために既存のテストを更新する。

  3. 不要になったテストや冗長なテストを削除するか、テストを組み合わせる。

  4. 一般的なケースを高いレベルでテストし、そのテストのバリエーションをより小さな、より低いレベルのテストに分割する。

  5. 明らかに必要な場合のみ、新しい高レベルのE2Eテストを追加する。

  6. 対象の機能が既存の種類のテストでカバーできない場合は、新しい種類の自動テストを導入する。

  7. 新しい種類の自動テストを可能にするために、システム・アーキテクチャを変更する。

  8. 開発チーム全体で、すべての種類のテストを含む包括的なテストスイートの健全性を継続的かつ批判的に評価する。

上記のポイント7は、健全なテスト自動化アプローチと機械的なテスト自動化の決定的な違いであるため、特に注目に値します。

テスト自動化をソフトウェアが構築された後にのみ適用されるものと考えるのはやめなければなりません。その代わりに、自動化はソフトウェア開発プロセスそのものの重要な一部であると考えるべきです。自動テストはソフトウェアとともに成長させなければならず、テスト自動化容易性の要件は、他の設計要件と同様に、システム設計とアーキテクチャを駆動するものでなければなりません。

このように自動化に取り組むことで、自動化を考慮せずに構築されたシステムでは利用できない、より小規模でコスト効率の高い種類のテストの自動化が可能になります。健全なアーキテクチャは、テストされることを前提に設計されています。システムが構築された後に自動テストを書くことだけでなく、システム設計者にこれらの要件を伝えることも自動化担当者の役割です。

すべての自動化にはコストがかかりリスクを生むこと、多くの異なる種類の自動テストで機能をテストするべきであること、テスト自動化の課題は最も効果的で効率的な自動化スイートを作成するためにそれぞれの種類のテストを適切に活用することであること、テスト自動化容易性はシステム設計において他の要件と同様に重要であることを理解すること、これがテスト自動化への健全なアプローチです。機械的にテストを自動化し、すべてのテストケースを新しい最上位レベルの自動テストとしてやみくもに自動化することは、健全なアプローチではありません。


参考資料:

インターネット上には、健全なテスト分布、テストの種類、テストへの投資など、この記事で議論されているさまざまなトピックに関する素晴らしい資料が数多くあります。しかし残念なことに、それらは多くの余分な情報、根拠のない憶測、マーケティング資料に埋もれてしまっています。

これらのトピックに関するおすすめのリソースをいくつかご紹介します。

The Practical Test Pyramid, Ham Vocker
https://martinfowler.com/articles/practical-test-pyramid.html

Testing Strategies for a Microservice Architecture, Toby Clemson
https://www.martinfowler.com/articles/microservice-testing/

The Diverse and Fantastical Shapes of Testing, Martin Fowler
https://martinfowler.com/articles/2021-test-shapes.html

Write Tests. Not too Many. Mostly Integration, Kent C. Dodds
https://kentcdodds.com/blog/write-tests

The Testing Trophy and Test Classifications, Kent C. Dodds
https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications

Testing Pyramid Ice-Cream Cones, Alister Scott
https://watirmelon.blog/testing-pyramids/

Round Earth Test Strategy, James Bach
https://www.satisfice.com/blog/archives/4947

Just Say No to more End-to-End Tests, Mike Wacker
https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html

Testing vs Checking, Michael Bolton and James Bach
http://www.satisfice.com/blog/archives/856

The Regression Death Spiral, Blake Norrish (yes, me)
https://medium.com/slalom-build/the-regression-death-spiral-18f88b9fb030

Test Cases are not Testing, James Bach and Aaron Hodder
https://www.satisfice.com/download/test-cases-are-not-testing

The Oracle Problem in Software Testing, A survey
IEEE TRANSACTIONS ON SOFTWARE ENGINEERING
https://discovery.ucl.ac.uk/id/eprint/1471263/1/06963470.pdf


いいなと思ったら応援しよう!