【読書メモ】Googleのソフトウェアエンジニアリング11, 12章

ちょっとずつ GoogleSWE 本を読んでいるので要約しながら所感をメモる。何においても本を読んだだけで実践できる人って最強だと思うのよね。本を読んだだけでいい気になっている人とは天と地の差がある。天側の人を目指そう。

今回読むのは以下の書籍である。

www.oreilly.co.jp

ちなみに英語版だと以下からオンラインで無料で読める。

abseil.io

今回の範囲は以下の2つ。やっぱ Google を支えるテストの話って気になる。

11章 テスト概観

11.1 何故テストを書くのか

内容: テストコードを書いてテストを自動化しよう。テストは定期的に頻繁に実行しよう。テストの誤りは迅速に修正しよう。テスト文化の醸成は生産性を向上させる。

所感: 自分も普段からテストを書くが、テストの利点として挙げられている「ドキュメンテーションの改善」は特に自分も感じるところである。テストは仕様の一部という意識を持つとよさそう。

11.2 テストスイートを設計する

内容: テストには規模(size)と範囲(scope)という2軸がある。

内容: テストスイートの重要な性質は速度と決定性(determinism)である。テスト規模(size)はこの2つの観点による分類であり、小・中・大の3つのテストからなる。

  • 小テストは、単一のプロセス内で完結し I/O 操作等を実行しないようなもの。テストの実行が早く非決定的な結果を生まないのが特徴。
  • 中テストは、複数のプロセスを使うもの。ただし localhost 以外のネットワーク呼び出しを許さない。非決定的な結果を生む可能性があるがリモートマシンへのアクセスがないので実行時間は膨大にはならない。
  • 大テストは、リモートマシンへの呼び出しができるもの。非決定的だし実行に時間もかかる。

たまに失敗するテスト(flake, flaky test)が増加するとテストへの信頼が喪失するので1%以下にすべき。テストは挙動を実行するために必要な情報のみを含むべき。テスト自体はシンプルに明確に保つべき。
所感: ここはテストの規模(size)についての話。テストの速度と決定性に着目するってのは自分では考えたこともなかった。

内容: テスト範囲(scope)は、テストによってどれだけの量のコードが検証されるかという観点である。小範囲のテストはユニットテスト、中範囲はインテグレーションテスト、大範囲はシステムテスト。前から順番に 80%, 15%, 5% の比を目指そう。

f:id:t-keita:20220112023237p:plain:w300

所感: この分類はわりと馴染みがある内容だが、理想的なバランスが数値として示されているのが Google の経験が詰まっている感じがする。

内容: コードカバレッジはあくまで動かしたコードの範囲であり、テストによって検証されたコードの範囲ではないので注意すべき。また、これらのテストメトリクスを高めることがゴールになってしまうのはダメ。 結局は、ユーザが期待する動作を総合的に考慮しテストスイートを評価することが重要。
所感: カバレッジを高めることがゴールになるのはソフトウェア開発あるある。真の品質を高めることが重要ってのはその通りだな。

12章 ユニットテスト

12.1 保守性の重要さ

内容: 保守性のあるテストとは "just work" する、つまり "普通に動く" ようなものである。具体的には、テストが失敗するまではケアする必要はないし、一方でテストが失敗したときは本当にバグを示すようなものである。脆いテスト(brittle tests)は、本当はバグを生んでいない修正に対して失敗するようになるテストである。テストの失敗が何を意味するのか不明なテストは望ましくない。
所感: テストっぽいものを作るのではく、テストとしてちゃんと役割を果たすものを作ろうという内容。当たり前の話だがチームで共有できていると強い。ちなみに日本語版で "just work" は「とにかく動作する」と訳されていたが、「動けばなんでもいい」という意味に取れるので訳が微妙だと思った。

12.2 脆いテストを防ぐ

内容: 脆いテストにはメンテナンスのコストがかかる。理想的なテストとは、システムの要件が変わらない限り変化しないようなテストである。リファクタリングなのにテストを変更するとか、新機能を追加するだけなのに既存のテストを変更するみたいな場合は怪しい。
所感: 脆いテストを避けるにはどうすればよいのか?そのプラクティスは次に続く。

内容: 脆いテストを避けるため、内部の構造に依存せず public APIs に対してテストを書くべき。テストでは、結果へ至る道筋(how)より、結果そのもの(what)を検証することが重要である。これによってリファクタリング等によってテストが失敗することを避けられる。この原則は Use the front door first principle と呼ばれる。
所感: private なメソッドをどうテストすべきか?というのはしばしば話題になる気がする。Google による見解としては、private なメソッドの粒度ではテストすべきではない、ということなのだろう。リファクタリングされることを前提にテストケースを作ることを意識したい。

12.3 明確なテストを書く

内容: 明確なテストとは存在目的と失敗理由が明確なものである。明確なテストには完全性(completeness)と簡潔性(conciseness)が重要である。

  • 完全性とは、結果に到達する過程を理解するのに十分に情報をテストが提供することである。たとえば、テストの結果の理解に必要な入力値は明記すべき。
  • 簡潔性とは、テストが無関係な情報を含まないことである。たとえば、テストの結果の理解に不要な入力値はヘルパーメソッドで隠蔽すべき。

所感: 意図が分かりやすいテストを書くためのポイントについて。テスト以外のコードを書くときにも有用なプラクティスだと感じた。

内容: テストはメソッドごとではなく挙動(behavior)ごとに書くべき。behavior-driven tests と呼ばれる。挙動は given, when, then の3つから構成できるので、テスト中にこの3つを明記するとよい。テスト名は冗長でもよいのでテストのアクションと結果を明記すべき。
所感: behavior-driven tests は Behavior-driven development とかの概念から派生したものだと思われる。個人的にはメソッドごとにテストしてしまいがちなので、もっと behavior を整理してテストケースを考えてみよう。

12.4 テストとコード共有:DRYではなくDAMP

内容: テストは説明的かつ意味が分かりやすいものであるべき。DAMP(Descriptive And Meaningful Phrases)が目指すべき姿。そのため、コードの繰り返しを減らす目的でコードを共通化すべきではない。ただし、テストしたい挙動と関係のない情報をヘルパーメソッドで隠蔽する形で共通する場合などはアリ。
所感: ヘルパーメソッドの使い方は上手いと思った。複数のテストで同じインスタンスを使い回すのではなく、関心のある部分だけ上書きするような構造にすることで意図が明確になる。

全体的な所感

Google のテスト思想やユニットテストのプラクティスがよく分かった。それほど難しいことはしていないように感じるが、メンバー全員が説明的なテストを作るってのは簡単ではないと思う。あと、カバレッジの話は少しあったが、ユニットテストを設計するときの観点とかは本書に書かれてないだけでノウハウがあるのかな。品質保証においてテスト観点は重要だし、上手く網羅的に設計できる人とそうでない人がいる印象がある。