望ましい自動テストとは: どのようなテストが開発生産性と開発者体験を共に高めるのか | Developer eXperience Day 2024レポート
2024年7月16日と17日の2日間にわたって開催された「Developer eXperience Day 2024」のセッションの中から、「望ましい自動テストとは: どのようなテストが開発生産性と開発者体験を共に高めるのか」のレポートをお届けします。
雑誌WEB+DB PRESSにてコラム「サバンナ便り」を連載された和田 卓人氏による「なぜ自動テストを書くのか」という問いかけに始まり、自動テストに求められる「信頼性の高い実行結果に短い時間で到達する状態を保つ」をキーワードとして、自動テストのアンチパターンや用語の曖昧さを踏まえつつ自動テストのあるべき姿を明らかにしていった。
登壇者
和田 卓人氏
タワーズ・クエスト株式会社
取締役社長
なぜ自動テストを書くのか
セッションは「なぜ自動テストを書くのか」という和田氏の問いかけから始まった。
よくあるアンチパターンとして、自動テストは「コスト削減」を目的とすると短期的には学習コスト、中長期的には保守コストがかかることで思ったような結果が得られず、結局手動テストに回帰しがちであると続けた。
本来の自動テストの目的とは変更容易性の高いソフトウェアによるアジリティの獲得である。ソフトウェアは変化することが当たり前なので自信を持って変化させ続けられる力が必要になる。
そこで和田氏はテストの目的として
「信頼性の高い実行結果に短い時間で到達する状態を保つ」ことで開発者に根拠ある自信を与え、ソフトウェアの成長を持続可能にすることと結論づけた。
以降のトークではこの結論を「信頼性の高い」、「実行結果に」、「短い時間で到達する」、「状態を保つ」の4つに分けてそれぞれを解説していった。
「信頼性が高い」自動テスト
信頼性の高い自動テストとは「嘘がないこと」と和田氏は述べた。
ここでの嘘とは以下の2種類に分類される。
- 偽陽性
- コードが悪くないのにテストが失敗する状況
- 例えるなら火が出ていないのに火災報知器が鳴る状況
- 偽陰性
- コードに問題があるのに見逃してしまう状況
- 例えるなら火が出ているのに火災報知器が鳴らない状況
プロダクトコードの正しい/誤っている、テスト結果が成功/失敗のツーバイツーのマトリクスで解説し、偽陽性と偽陰性を可能な限り減らしていくことで開発者の生産性を上げることができると和田氏は続けた。
この偽陽性と偽陰性について和田氏は以下のパターンに分類して説明した。
- 偽陽性
- 信頼不能テスト(flaky test)
- 何もしなくても失敗する可能性のあるテスト
- 脆いテスト (brittle test, fragile test)
- 少しでも手を触れると失敗するテスト
- 信頼不能テスト(flaky test)
- 偽陰性
- 空振り
- テストしているつもりでできていなかった
- カバレッジ不足、テスト不足
- 書かれるべきコードが書かれていない
- 空振り
- 自作自演
- 不具合のあるコードと同じコードで期待値を定義している
自作自演の偽陰性についてはJavaScriptのサンプルコードを交えながらの解説もあった。
実行結果について
自動テストの実行結果とは何か、和田氏はこれを単なる実行結果ではなく「情報」であるべきと述べた。ここでの「情報」とは単なる実行結果ではなく人間に行動を促すもの。
仮にテストの結果に嘘がなければ次の行動は「デプロイ、マージ」、「問題箇所の特定と修復」の2択に絞ることができる。
また、和田氏は自動テストの失敗としてExecution ErrorとAssertion Failureの2種類があり、Assertion Failureの方が調査の難易度は若干高いと述べた。このAssertion Failureを調査する上でAssertionの書き方が重要になる。「Assertionの書き方は失敗時に輝く」とAssertionを正しく記述することの重要性を強調し、具体的なコードを交えながら説明した。
短い時間で到達する
和田氏はこのチャプターの説明を始めるにあたり、聴講者に以下の様な問いかけを行った。
- データベースにアクセスするのはユニットテスト? Yes / No
- ネットワークにアクセスするのはユニットテスト? Yes / No
- ファイルにアクセスするのはユニットテスト? Yes / No
- 現在時刻にアクセスするのはユニットテスト? Yes / No
- 依存先のモジュールに本物を使うのはユニットテスト? Yes / No
オンライン、オフラインの聴講者から寄せられた多数の回答にはばらつきがあった。
和田氏もこれらの問いには正解がなくチームによって異なるものとし、一方でこのユニットテストという言葉は今日まで厳密な同意が取れていないまま使われてきているということを歴史的な背景を交えながら説明した。
ユニットテストという定義が曖昧な用語に代わるテストの分類として和田氏はテストサイズという軸を紹介した。
テストサイズとは『Googleのソフトウェアエンジニアリング』によると以下の様に分類される。
- Small
- テストの実行が1つのプロセスに収まっている
- Medium
- 1つのプロセスに収まっていないが、1つのマシンに収まっている
- Large
- 1つのマシンに収まっていない
このテストサイズという分類はユニットテストとは異なり、定義が非常に明確である。
このテストサイズという分類と従来用いられてきたUnit, Integration, E2EのTest Scopeを3×3の表にまとめたものを和田氏は提示した。
この表の縦軸は上からUnit, Integration, E2E、横軸は左からSmall, Medium, Largeとなっている。この表の右上ほど費用対効果が悪く、逆に左下ほど費用対効果が高くなる。
「状態を保つ」ために
「信頼性の高い実行結果に短い時間で到達する」状態を保つためにどの様にするべきか。和田氏はテストピラミッドを引き合いに出しながら望ましいテストの比率について解説を始めた。
一般的にはテストピラミッドはE2Eを上段、インテグレーションテストを中段、ユニットテストを下段とし、下に行くほどテストケースは多くなる。
しかし、このテストピラミッドという考え方は20年以上も前に提唱されたものであり、インテグレーションテストを手厚くしたトロフィー型やハニカム型といった別の比率が提唱され盛んに議論されており、現場の開発者に混乱を引き起こしている。
和田氏はこれらの混乱の元は前述の「ユニットテスト」、「インテグレーションテスト」の解釈のブレから生じているものとした。
では、ブレのない定義とは何か。ここで和田氏はLarge, Medium, Smallのテストサイズをテストピラミッドの考え方に当てはめた。これにより定義は明確で混乱が生じなくなるとした。
また、和田氏はテストサイズを当てはめたテストピラミッドがビルドパイプラインにもたらすメリットについても述べた。
では、この理想的なピラミッド型を実現するためにはどうするべきか。和田氏はテストのサイズダウン戦略の解説を始めた。
多くの現場ではテストの比率はアイスクリームコーン型(テストピラミッドの逆でアンチパターン)から始まる。これ自体は悪くはないことだが、なぜE2Eテストが重視されてしまうのかの背景には開発チームとQAチームが分かれていることがあり、良かれと思ってそのアンチパターンに陥ってしまっていると和田氏は説明した。
和田氏はE2EテストがTechnology Radarで「HOLD(積極的な投資は避けるべき)」に指定されたことを引き合いに出しながらE2Eテストでなんでもカバーしようとすることへの危険性を述べた。
次にどのようにテストサイズを小さくしていくかについて具体的な例を挙げていった
- LargeからMediumへ
- テストダブルを活用して1つのマシンに収める
- MediumからSmallへ
- 良い設計(低結合、高凝集)をする
- テストしにくい箇所を切り離して1プロセスでテストできるようにする
良い設計をすることで品質は上がる。和田氏は関氏の「テストはあくまでも品質上げるきっかけ。品質を上げるのはプログラミング」という名言を紹介し、「テストは体重計の様なもの。体重計に乗っても痩せないが、痩せるきっかけにはなる。」と付け加えた。
講演のまとめ
和田氏は最後に講演の結論としてテストサイズを当てはめたテストピラミッドを提示しながらLargeからMedium、MediumからSmallへテストサイズを小さくしていくことが「信頼性の高い実行結果に短い時間で到達する状態を保つ」ことにつながるとした。
所感
エンジニアとしての経験を振り返ると、これまで経験してきたいくつかのチームでもユニットテスト、インテグレーションテスト、E2Eテストといったテストを分類する用語はチームやシステムによってまちまちでどこからどこまでテストするのか、何をしてはならないのかが曖昧でした。
あるチームは依存対象の全てのモジュールをモック化したテストのことをユニットテストと呼んでいたり、別のチームはユニットテストで別システムへの連携をテストしていたりもしていました。
今回のトークではテストサイズという明確な基準によってテストを分類し、それらをテストピラミッドに当てはめることでこれまで曖昧であったテストピラミッドのあるべき状態を明確なものに落とし込むことができている部分が非常に興味深いものでした。
テストサイズによるテストピラミッドはチームに対して明確な基準を示すことができるため、この考えをチームのテスト戦略に取り入れることで、新しく参画したメンバーに対して「このテストでは何をテストして何をすべきではない」という基準を示すことができ、また巨大なE2Eテストを大量に用意して日々大量のエラー通知(実際にはなんの問題もない)に悩まされたり、エラー通知を無視する習慣がついて致命的な問題を見逃すといったリスクを軽減できると感じました。
また、テストと体重計の比喩など随所に挟み込まれる和田氏の例え話や具体例も非常に理解しやすく印象的なものでした。
今回のトークはエンジニアが
- なぜ自動テストを作るのか
- あるべきテスト、避けるべきテスト
- チームがより良いテストの習慣を会得するには
に対して明確な解を持つ一つの指針になるのではないかと感じました。