こんにちは、kubopです。
Googleにはトイレテスト(TotT)という文化があるようで、テストに関するTipsをトイレに貼り出し、テストに関する知識を全社で共有しているらしいです。
昨今はリモート勤務が広まり、TotTの実施は難しく、SlackやBotを用いてもなかなか浸透するかどうか…
そこで、noteに書きつつ自分が勉強するために、少しずつ読んで内容や、所感を書いてみようと思います。
※ 翻訳・解釈の間違いなどあるかもしれません。
その場合はこっそり教えてください。
Just Say No to More End-to-End Tests
テストの世界では、エンドツーエンドテストを中心に構築されたテスト戦略が、しばしば失敗に終わることがあります。
テスターは、ユニットテスト、統合テスト、E2Eテストなど、多くの種類の自動テストを書くことに時間を費やします。
その際に、製品やサービスを全体としてユーザーのシナリオをシミュレートするE2Eテストを重点的に作成します。
理論上のE2Eテスト
E2Eのテストに依存するのはあまり良い考えでは無いですが、理論的には理にかなっています。
"Google's list of ten things we know to be true(Googleの、私たちが知っている真実の10項目)"の1つには
"ユーザーに焦点を当てれば、他の全ては後からついてくる。"という項目があります。
従って、ユーザーに焦点を当てたE2Eテストは、素晴らしいアイデアのように聞こえます。さらに、この戦略は多くの人にとって納得感のあるアイデアとして受け入れられます。
開発者は、テストの大部分を他人に任せることができる点で
マネージャーは、実際のシナリオをシミュレートし、失敗した場合にユーザーに与える影響を簡単に判定できる点で
テスターは、バグを見逃したり、実際の検証していないテストを心配しなくても良くなる点で
E2Eテストを好みます。
E2Eテストの実践・例
このテスト戦略は理論的にとても良いものだとするならば、
実際にはどこで失敗したと言えるのでしょうか?
以下のチームは良いテストインフラを所有していると仮定しましょう。
毎晩サービスの最新バージョンがビルドされ、
テスト環境にデプロイされ、全てのE2Eテストはこのテスト環境で実行され
さらにテスト結果をまとめたレポートがチーム内で共有されます。
次のリリースに向けて新機能を実装するために、あと1日で少なくとも90%のE2Eテストをパスしなければならない状況を想像してみてください。
以下は、リリースまでの残り日数(マイナス値はオーバー日数)とE2Eテストのその日の結果、Pass率を示したものです。
これらは、多くの問題を抱えながらも最終的には不具合を発見することができ、ユーザーに届く前に修正することができました。
しかし、マイルストーンを1週間遅れさせ、多くの時間外労働を要しました。
E2Eテストで失敗してしまったテストの根本原因を探すのは大変で、時間がかかります。
さらに、多くの些細なバグが大きなバグで隠されていて、しかもE2Eのテストが不安定になり、開発者は修正がうまくいったかどうか翌日まで待たざるを得ませんでした…。
E2Eの問題点は以下の通りです。
これらの問題がE2Eテストにあるとわかりました。
では、テストへの正しいアプローチとはなんでしょうか?
テストの真の価値
一般的にテスターの仕事はテストが失敗した時点で終了です。
バグが報告され、そのバグを修正するのは開発者の仕事です。
しかし、E2Eはその境目が曖昧になり、どの部分の戦略が破綻しているのか特定することが難しい。(ここはかなり解釈違いがあるかもですが)
テストが失敗しても、ユーザーには直接の利益はありません。
製品が機能すれば、テストが機能すると言おうがしまいが、それは機能します。
製品が壊れていれば、テストが壊れていると言おうが言うまいが、それは壊れているのです。
テストに失敗してもユーザーのためにならないのであれば、何がユーザーのためになるのでしょうか?
bugfixは、直接的にユーザーに利益をもたらす。
ユーザーは意図しない動作、つまりバグがなくなって初めて満足します。
当然ですが、バグを修正するにはそのバグの存在を知らなければならず、
さらにバグの存在を知るにはそのバグを捕捉するテストが必要です。
テスト -> バグ発覚 -> バグ修正
このステップの中で最後のバグ修正でしか、価値を提供出来ません。
このように、どのようなテスト戦略で合っても大切なのは「開発者がどのようにバグを修正・予防出来るか」です。
正しいフィードバックループの構築
テストは、製品が動作しているかどうかを開発者に知らせるフィードバックループを作ります。
理想的なフィードバックループは以下の特性を備えています。
高速である。
信頼性が高い。
失敗を切り分けることができる。
小さく考え、大きく考えない
ユニットテストを作成する
ユニットテストは製品の小さな断片を取り出し、その断片を単独でテストします。
このテストは理想的なフィードバックループを作り出すことができます。
ユニットテストは高速で、信頼性が高く、失敗を分離します。
また、hermetic unit testは、Flakyなテストを完全に取り除くことができます。
hermetic unit test
Googleの新人が研修で行うストップウォッチのテスト
E2Eテスト VS ユニットテスト
E2Eテストでは、まず製品全体がビルドされ、次にデプロイされ、最後にすべてのE2Eテストが実行されるのを待たなければなりません。
E2Eテストは実際のユーザーシナリオをよりよくシミュレートしますが、この利点はE2Eフィードバックループの遅さで打ち消されてしまいます。
統合テスト
ユニットテストには、単体でうまく動作しても、それらがうまく連動するかどうかわからないという大きな欠点があります。
しかし、それでも必ずしもE2Eのテストが必要なわけではありません。
そのような場合は、統合テストを使用します。統合テストでは、小さなユニット群、多くは2つのユニットを取り上げ、それらの動作を全体としてテストし、それらが首尾よく一緒に動作することを検証します。
E2Eテスト : 統合テスト : ユニットテスト = 10% : 20% : 70%
といった配分でテストを構築する必要があります。
(これは以下を参照したほうがはやいため、割愛)
コメント追記:
🤔
コメント欄がかなり白熱していて読むのが大変でした。
かなりラディカルな意見もありましたが、E2Eテストが必要となるケースも、少なからずあるし、全てを無くせと言っているわけでは無いと思いました。
ユニット・統合テストが全く無い状態であれば、リグレッションを防ぐために手始めにE2Eテストを書きながらリファクタするという手段もあるし、
E2Eテストに時間がかかりすぎているなら、適切にユニット・統合テストに落として高速でまわせるようにする、といった手段があるなと思います。
テストピラミッドは絶対的な指標なのではなく、フェーズと状態を鑑みてどちらを増やすか、減らすかを戦略的に考えていく必要があるなと思いました。
単体テストのFIRSTの特性については知らなかったので、後で調べてみようと思います。
追記:
CICDのモニタリングをピラミッドの頂点におきたいというコメントは、以下のDevOpsバグフィルターに近い考えかもしれないです。
Licensed under a Creative Commons
Attribution–ShareAlike 4.0 License