『単体テストの考え方/使い方』Vladimir Khorikov https://amzn.to/3BCLytq を以前読んだ。
そのこともあり、これまで1章を読んだが、次は2章ではなくテストについて記されている11章を読もうと思う。前の章(第1章)を読み終わったのが2024年11月1日(金)なのでこの日にスタート、とする。
第3部 プロセス
8章からが第3部である。8章から11章までは未読だが、これらは飛ばして11章を読む。
11章 テスト概観
しかし、「バグを捕捉すること」は、テストの動機の一部に過ぎない。ソフトウェアのテストを望む理由として同等に重要なものは、変化を可能とする能力を備えておくため、という理由である。
『単体テストの考え方/使い方』でも単体テストで成し遂げたいもの、目的、ゴールとして、次のように書かれている。「その答えは、ソフトウェア開発プロジェクトの成長を持続可能なものにする、ということです。」(1.2 なぜ、単体テストを行うのか? P7)。やはり実装時のバグ検出だけではなく、その後の開発のやり易さに焦点をあてている。
11.1 何故テストを書くのか
テストの価値は、エンジニアがテストに寄せる信頼に由来してることを心に留めておくべきだ。
『単体テストの考え方/使い方』で述べられてきたことが簡潔に表現されていると感じた。ただ単にテストを増やすのではなく、良いテストを整備していく必要がある。
だめなテストスイートは、テストシートが全くない場合より価値が悪い。
『単体テストの考え方/使い方』にも同様の記述があった。どこかは忘れた。だが、「1.2.1 何がテストの質を良くし、何がテストの質を悪くするのか?」あたりを読むと良い。
Googleでは、テストは後からの付け足しであってはならないと定めている。
11.1.1 Googleウェブサーバーの物語
これらの問題に対処するため、GWSのテックリード(TL)は、エンジニア主導で行われる自動テストについてのポリシーを制定することを決めた。 …略… このポリシーを制定して1年以内に、緊急の本番コードリリースの数は半減した。
本書第1章で出てきたよくスケールポリシーだったのだと思う。ポリシーの威力重要性がわかる事例。
GWSの経験が教えてくれた鍵となる知見は、製品の欠陥を避けるのにプログラマーの能力のみに頼るわけにはいかないということだった。
どれだけメンバーが優秀であったとしても、それだけでバグを押さえ込むことはできない、という、言われてみれば当たり前なのだが、よくわかる事例だ。
最も優れたチームというものは、所属するメンバーたちの集団的英知を全チームのための利益に転ずる方法を見出す、それがまさに、自動テストの行うことだ。
ウィキなどナレッジベースナレッジシェアとして有効活用しているが、自動テストも同じような効力を発揮できる、という視点になるほど確かに、と思った。
11.1.2 現代的開発速度でのテスト
手作業で全機能と触れ合うことを人間に依頼して製品の品質を評価しようとする試みは、とにかくスケールしない。テストに関しては、明確な答えが1つあり、それは自動化である。
同意。
11.1.3 書き、実行し、反応する
当たり前だが、テストを書くことと、良いテストを書く事は別の話だ。
これも『単体テストの考え方/使い方』にも同様の記述があった。『単体テストの考え方/使い方』ではユニットテストが誕生した時と比べ、現在ではテストコードを書くのは当たり前になっており、いかに良いテストを書くようにするかが焦点に当たっていると言う話も触れられていた。
根本的には、自動テストは同じ動作を何度も何度も繰り返すことから成っており、何かが破綻する場合にしか人間の注意を要しない。
『単体テストの考え方/使い方』では退行に対する保護にあたる話。
テスト失敗の数分以内にその破綻したテストを修正することを優先するチームは、信頼性を高く保つとともに失敗の切り分けの速度を高速に保つことができ、ひいてはテストからより大きな価値を引き出せる。
あるメンバーから他のメンバーにプロジェクトを引き継いだとき、引き継いだメンバーがテストをメンテナンスしないことによって破綻したテストが多くできてしまった、ということがあった。テスト文化が育っていないのだと思う。
11.1.4 コードをテストする利点
決定的に重要なのは、テストされているコードは、その存続期間を通じて欠陥が減少することである。 …略… プロジェクトの存続期間を通じ、一度書かれたテストは配当金支払いのように利益を定期的に生み続ける。
「テストされているコードは欠陥が減少」を「一度書かれたテストは配当金支払いのように利益を定期的に生み続ける。」と表現するのは使えそう。
既存の挙動を保ちつつコードをファクタリングする変更は、既存テストへの変更を(理想的には)何も必要とすべきではない。
これを簡潔に表現したのが見出しの「変更への信頼の増大」。なるほど。
一度に1つの挙動を動作させる、明確で、対象を絞ったテストは、実行可能なドキュメンテーションとして機能する。 …略… テストがドキュメンテーションとしての効果を最も発揮するのは、ドキュメンテーションを明確簡潔に保つように注意が払われている場合のみであることに注意してほしい
ユニットテストがドキュメントとして機能するためには条件がある。それは1度に1つのことをやっている、明確である、対象が絞られている、の、3つの条件が必要ということに注意しておきたい。
健全な自動テストスイートがあれば、チームは、担当アプリケーションの新バージョンを自信を持ってリリースできる。
見出しは「高速で高品質なリリース」。テストが開発を加速する。自信、保証、があるから。
11.2 テストスイートを設計する
我々が相当早い段階で学んだ教訓は、エンジニアは比較的大規模な、システム規模のテストを書くことを好むが、それらのテストはより小さいテストより時間がかかり、信頼性が低く、デバッグが難しいと言うことだ。
比較的大規模なテストを書いているのに、自分が当てはまる。クラス単位の小さなテストを書くよりも、複数の処理のまとまりをAPIを呼び出すことで一気にテストすることが日常的になっている。
我々は、あらゆるテストケースについて、2つの別個の次元があるという結論に達した。それは、規模(size)と範囲(scope)だ。規模は、テストケースの実行に必要なリソースを差し、メモリー、プロセス、時間等だ。範囲は、検証している特定のコードパスを指す。コードの行を実行することは、それが期待通りに動作したのを検証することとは異なるという点に注意してほしい。規模と範囲は相関しているが、別個の概念である。
なんとなくだが、ここら辺が『単体テストの考え方/使い方』に書かれていないことに触れているような気がする。読むのが楽しみだ。
11.2.1 テスト規模
すぐに詳細入るがとりあえず簡潔に述べると、図11-2に示されるように、小テストは単一のプロセス内で実行され、中テストは単一のマシン上で実行され、大テストは任意の好きな場所で実行される。 我々は上記のように区別しており、もっと伝統的な「ユニット」または「インテグレーション」の区別は用いていない。その理由は、我々がテストスイートに望む最も重要な特性は、テスト範囲とは無関係に、速度と決定性(determinism)だからである。
本書が出版されたのは2021年11月26日、『単体テストの考え方/使い方』が出版されたのは2022年12月28日。という事は、『単体テストの考え方/使い方』の著者は、テストサイズによる分類の仕方を知ろうと思えば知れたことになる。あえて取り入れなかったのか、間に合わなかったのか、取り入れなかったのだと思うが、想像すると面白い。
6 訳注: すべての事象は、既存の原因によって完全に決定されていると言う世界観で、コンピューターサイエンスでは決定性(determinism)アルゴリズムと言う時、そのアルゴリズムは特定の入力に対して常に同じ出力を行う。決定性に対して非決定性(nondeterminism)があり、非決定性(nondeterminism)アルゴリズムは、入力が同じでも、実行の度に出力が異なりうる(確率的アルゴリズムやマルチスレッド処理等)。13章参照。
関数型プログラミングに通ずる考え方だ。
11.2.1.1 小テスト
主な制約は、小テストは単一のプロセス内で実行されなければならないというものだ。 …略… これは小テストはネットワークまたはディスクへのアクセスを許可されていないことを意味する。
定義。ディスクへのアクセスというのは、ファイル操作のこと。ファイルを作ったり更新したり削除したりはダメということ。
これらの制約の目的は、小テストに、テストの遅さや非決定性の主要な原因となっているものへのアクセスが存在しないことを確実にするというものだ。 …略… 小テストの制約は、エンジニアが墓穴を掘るのを防止するサンドボックス(sandbox)を提供している。
小テストは早くて決定性がある。だから、単一プロセスでやる。『単体テストの考え方/使い方』でのユニットテスト(「単体(unit)」と呼ばれる少量のコードを検証する。実行時間が短い。隔離された状態で実行される)に相当すると感じた。
Googleでは、テスト範囲の大小に関係なくエンジニアに可能な限り小テストを書くように努めることを勧めているが、それは小テストによりテストスイート全体が高速に信頼性のある状態で実行されるように保たれるからである。
小テストは、高速と信頼性。
11.2.1.2 中テスト
換言すれば、中テストは単一マシン内に収まっていなければならない。
開発者が自分のマシンで行える自動テストの限界がここになる。
残念ながら、柔軟性が増すのに伴い、テストの速度が遅くなり非決定性になる可能性が増すことになる。
これは実感がある。データベースをリセットしたり、データを用意するのに時間がかかる。
11.2.1.3 大テスト
大テストは中テストにされているlocalhostの制約を取り払い、テスト並びにテストされているシステムが複数マシン上にまたがることができるようにしている。
個人でやるには規模がでかすぎる。組織でやるというイメージがある。
Googleのチームは、しばしばチームの大テストを小テストや中テストから分離し、大テストは開発者ワークフローに影響しないようにビルドとリリースのプロセスの間にしか実行しないようにしている。
大テストは開発者が普段意識しない位のものになっている。
ケーススタディー : 信頼不能テストはコストが高い
我々の経験が示唆するのは、信頼不能性が1%に接近するとテストは価値を失い始めると言うことだ。
おお、これが講演の動画などで出てくるところの原文だ。
ほとんどの場合、信頼不能テストは、テスト自体の非決定性の挙動のために現れる。 …略… つまりクロック時間、スレッドスケジューリング、ネットワーク、レイテンシー、その他だ。
大体が対象コードが外部に依存している箇所、という感じがした。
11.2.1.4 全テスト規模に共通の属性
全てのテストは密閉された状態となることを目指すべきである。
依存しない、完結した状態にするということだと思う。
テスト自体にはテストがないため、テストはその正しさについての重要なチェックとして手作業でのレビューを要する。このことを必然的帰結として、テスト内で条件分岐やループ (loop : 繰り返し) のような制御フロー分を利用することは強く非推奨となっている。
テストコードは読んだときに自明となるように書く。その理由の1つにテスト自体にはテストがないからが、ある。
テストは往々にして、何かが破綻する場合のみ顧みられるということを心に留めておくべきだ。
テスト失敗したときだけテストコードが読まれるということを思うと、大体の人はテストコードに何が書いてあることなど覚えていない。よって見るたびに初見であり、よって読みやすいと言うことが大事になってくる。
テスト規模の実際 : テスト規模の正確な定義があることで、それを強制するツールを作れるようになった。
定義ができるようになったことによりツールが作れるようになった、というのは面白い。この観点、流れは覚えておくと応用が効きそう。
11.2.2 テスト範囲
Googleでは、より小規模のテストを推奨するのと全く同様に、エンジニアにより狭い範囲のテストを書くように推奨している。非常に大雑把なガイドラインとしては、テストの約80%がビジネスロジックの大部分を検証する狭い範囲のユニットテスト、15%が2つ以上のコンポーネント間の相互作用を検証する中範囲のインテグレーションテスト、5%が全システムを検証するエンドツーエンド (E2E) テストと言う混合比を目指そうとしている。図11-3 はこれをピラミッドとして視覚化させ、図示したものだ。
テストピラミッドで考えるのをやめてテストサイズを採用すると良さそうというのが講演の動画などで示されたこれからの指針だと私は解釈していたが、おそらく浅かった。それぞれ交わることのあまりない独立した概念だった、とはいえ、テストピラミッドに苦しむ現実では、テストサイズをまず導入し、といった、独立はしているが塩梅を意識することで問題を解決していこうとするものなのではないかと、この部分を読んで思った。
製品が発展するにつれて、より大規模なテストは健全性チェックとして働く。しかし大規模テストは、バグを捕捉するための主要な方法とみなされるべきではない。
『単体テストの考え方/使い方』でいう「退行 (regression) に対する保護」に該当する (本書では大規模なテストではといっており、『単体テストの考え方/使い方』では P108 にて良い単体テストを構成する柱という、異なる文脈で語られてはいるが) と思った。テストにより品質が上がるわけではないが、品質が下がることを防いでくれる。
11.2.3 Beyoncéルール
換言すれば、システムが特定の挙動を示すことについて確信を持ちたいなら、それを確実にする唯一の方法は、そのために自動テストを書くことである。
他チームシステムの機能を利用するときに仕様の想定外の実行方法で壊れるのであれば、それで進めようとしているあなたが他チームシステムのコードを修正しテストも書きなさい、ということだと思う。ここで書かれているルールはまだ腹落ちしない。
11.2.4 コードカバレッジについてのメモ
それは、コードカバレッジはコードの行が呼び出された事を計測するだけで、結果として何が起こったかは計測しないからである (比較的大規模なテストを実行する際に起こるカバレッジのインフレーションを避けるために、カバレッジ計測は小テストに留めておくことを我々は推奨している)。
カバレッジを見ることでテスト実行されていない部分はわかるが、それによってシステムが適切にテストされているいないを結論づけることはできない。
11.3 Google規模でのテスト
Googleが運営するあらゆる製品とサービス向けのコードのほぼ全行が、1つの場所に全部保存されている。今日、リポジトリー内には20億行以上のコードがある。 …略… Googleのコードベースは毎週2,500万行近くの変更を経ている。
へー、と思った点。
社内のコードベースの開放性は、全員にコードベースに対する責任を持たせるというレベルの共同オーナーシップを促進している。
コードやシステムに問題を感じるのであれば、苦情を言うのではなく、自ら直してプルリクするということだと思う。おそらくこれはBeyoncéルールの一側面じゃないかなぁと、正解かどうかはさておき、なんとなく思った。
Googleが他の組織と少々異なる点として他にも、リポジトリーのブランチ作成を行うチームがほぼ存在しないと言う状況がある。
GitHubフローといった開発フローを採用しないで、直接マスターにどんどんコミットするという意味だと思うのだが、にわかには信じがたい。コンフリクトは起きないのだろうか?ちょうど16章「バージョンコントロールのブランチ管理」という章があるので、これを読めばわかるだろう。
11.3.1 大規模テストスイートの落とし穴
脆いテストの最も有害な問題は、モックオブジェクトの誤った利用から生じる。
という事は、できるだけ自動テストではモックオブジェクトを使わない方向なんだと予想する。きっと「古典学派」よりなんだろうな。
テストスイートの実行が遅くなればなるほど、実行される回数は減り、テストスイートが提供する利益も減る。
自動テストを速く動かすと言うのは速く動けば嬉しいと感じる以外に、単純に、速く動かないと価値が下がる、速く動けば価値が上がる、に直結していた。
大規模なテストスイートと共存する秘訣は、そのテストスイートを敬意を持って扱うことである。エンジニアたちに、自分たちのテストのことを気にするよう仕向けるインセンティブを与えるべきだ。したがって、エンジニアに対し、素晴らしい機能のローンチに際して与えるのと同じだけの報賞を、盤石なテストを備えることについても与えるべきである。
技術的な話から、人事評価のような話が出てきた感じがする。それだけ重要で全体的な話と言うことだと思う。
11.4 Googleでのテストの歴史
Googleがどのようにしてそこにたどり着いたのか学べば、啓発される点があるかもしれない。 …略… 事実、2005年まで、テストは規律として化されたプラクティスというより、物珍しいものに近かった。 …略… しかし2005年から2006年にテスト革命が起こり、それが社内のソフトウェアエンジニアリングへのアプローチ方法を変えた。
Googleも最初からテスト文化があったわけじゃないと言うことをここでも改めて言っている。
11.4.1 オリエンテーション講習
社内の新入社員全員に働きかけることができれば、文化面での変化をもたらす極めて効果的な手段となりえた。幸いにも、新入社員であるエンジニア全員が通る単一のボトルネックが当時存在し、そして現在に至るまで存在する。それはオリエンテーションだ。
言われてみれば、と思った。自分の置かれた環境では、新入社員は多くなく機会も少ないので活かせない。代わりにプロジェクトの始まりなどで応用できたら良いかもしれない。
最も重要な事は、それらの観念全てが、社内で標準のプラクティスであるかのように提示されたことだった。新入社員たちは、そうした講習が、疑うことを知らないチームの内部へテストの観念を忍び込ませるトロイの木馬として使われているとは夢にも思わなかった。
中途で入社した社員が新しいプラクティスを広めていって、その中途入社社員自身は、「え?これ常識でしょ?」みたいなふうに思ってる、これと通づる感覚だなぁと思った。こうしてほしいと依頼するよりも、こういう風にやったからあなたも合わせてね、とする方が人間は動きやすいのかもな。
11.4.2 テスト認定プログラム
このプログラムがチームに与えるのを目指していたのは、自分たちのテストプロセスの成熟度を理解する方法と、そしてもっと決定的に重要なものとして、テストプロセス改善方法の各種手順書をまとめたものだった。 …略… 社内ダッシュボードは、全チームのレベルを表示することにより、社会的な圧力をかけた。チームが互いに競い合って、プログラムでは段階をあげていくのに時間はかからなかった。
要するにインセンティブを与えて競わせると言う事。
11.4.3 トイレでのテスト
TotTのゴールはかなり単純で、全社でのテストに関する意識を積極的に高めるというものだった。問題は、従業員が世界中に散らばった企業でそれをやる最良の方法はどんなものかという点だ。
「従業員が世界中に散らばった企業でそれをやる最良の方法」が気になる。
軽いブレインストーミングの後で、誰かが冗談で、トイレの個室にチラシを提示すると言うアイディアを提案した。我々はすぐにそのアイディアの天才的なところを認識した。トイレは、誰もが何があっても日に一度は訪れなければならない場所なのだ。冗談だろうがそうでなかろうが、そのアイディアは実装するのに申し分のないほど低コストだったので、試さない手はなかった。
低コストに注目したのか。確かに。リモートワークでは実現しにくそうだな。。。
2006年4月、Google社内全体のトイレの個室にPythonでのテストを改善する方法を扱う短い記事が現れた。 …略… 元のTotT作者達は軽い編集を遅くした版を公開し(https://oreil.ly/86Nho) Googleの経験が広く業界に共有されるよう促した。
リンク先は https://testing.googleblog.com/search/label/TotT 。これを読んでいくと技術的にパワーアップできそう。
11.4.4 今日のテスト文化
なぜテストを書くことを命令して強制するところから始めなかったのだろうか。 …略… おそらく進歩を鈍化させるものだ。 …略… 成功する思想は広まるものであるというのが我々の信念であり、したがって専念すべき点は、成功を実証してみせることとなった。
この辺は文化に関わることなので、アプローチ方法は組織によってまちまちなのだろうと思う。
11.5 自動テストの限界
例えば、複雑なセキュリティ脆弱性の探索は、人間の方が自動システムより上手だ。 …略… よく理解されている挙動を扱うために自動テストを用いると、人間のテスターの高コストかつ定性的な労力を、人間のテスターが最大の価値を提供できる製品部位に集中させることができ、テストの過程で人間のテスターを涙が出るほど退屈させずに済む。
既に知られている事を繰り返し高速にテストするのに、自動テストは優れている。未知の問題や複雑で難しい事のために人間の労力を空けておく。
11.6 結論
会社が創業以来、ほぼ100倍の倍率で発展してきたにもかかわらず、品質とテストへの我々のコミットメントは今日、かつてないほどに強まっている。
やっぱり自動テストを上手に使うと、それだけおいしいと強調。
11.7 要約
- 自動テストは、ソフトウェアの変化を可能とするための基盤となる。
自動テストは品質云々よりも「変化を可能にする」ことに役立つというところを強調しているところに今一度注目したい。
おわりに。11章を読んで。
多分12月1日(日)くらいに読み終わった。今12月5日(木)だが、次の章の最初の部分を読んでいる。
さて、一番得たもので大きい部分、印象的であった部分を挙げる。
テスト規模はテスト範囲の進化系と思っていたが、そうではなく独立した存在であると、整理できたのは大きかった。その上でテスト希望を軸にテストを整理していくと便利だと、おそらく講演の動画では言及していたのではないか、と考え直すきっかけになった。
ちなみに、今のペースだと1ヶ月で1章。25章あるので2年以上かかる試算になる。ずっとこの本ばかり読むのも飽きるので、テストの部分を読み終わったら次の本に手をだす、などとしても良さそうだと感じている。
