未来のいつか/hyoshiokの日記

hyoshiokの日々思うことをあれやこれや

テストを書くこととテストをすることの違い

会社でレガシーコード改善ガイドの読書会をやっていて、次回で読了だ。4月に入ってから週に1回くらいのペースでやっていて、2ヶ月半くらいかかった。途中、ゴールデンウィークや所用で開催しないこともあったので、10回くらいで完走したことになる。

一人当たり、1章ないし2章くらいを担当して、その章に書いてあることを説明した後にみんなであーだこーだ議論をする。気になったことを質問したり、どうも良く分からないことをみんなで考えたりする。

テストがないコードはレガシーコードだ!というキャッチフレーズはわたしの心をとらえた。

参加者の皆さんとその価値観を共有できた事はうれしい。

現場での開発の実情をいろいろ教えてもらった。テストを書くことはあまり一般的ではないということにわたしは衝撃を覚えたのであるが、この読書会を通じて、テストを書かない開発というのがレガシーコードを作っている事に他ならないという共通の認識を得たことの価値は大きい。

システム変更の方法は、大きく2つに分けることができると本書は主張する。ひとつは、「編集して祈る」、もう一つは「保護して変更する」方法である。

編集して祈るというのが残念な事に業界標準になっている。つまり変更作業をして、その機能を何らかの方法で確認して、周辺もざっと調べて特に問題がないことを確認して、結果がうまくいくことを祈るという方法である。祈るのである。

一方の保護して変更するというのは、足場を組んで、転落防止の柵や網をはって、転落しないようにしたり、万が一転落してもセーフティーネットによって保護されるというようなものである。ソフトウェアで言えば、テストで保護するということである。テストプログラムによって、意図しない破壊が起こっていないことを確認するのである。


バグとテストとデバッグ

バグというのはソフトウェアの期待する動作と実際の動作の差分である。期待する動作というのは、通常何かの文書、例えば要求仕様書などに、書かれているものであるが、そのような文書が存在しない場合もあるし、そもそも日本語で明確に書けないような非機能要件というのもある。まあ期待値との差が出たらそれはバグと考えるというのが大雑把な理解である。*1

そのようなバグを発見する作業がテストという作業である。

テストを行うために、テストプログラムを書いて、それを実行し、期待する動作と実際の動作を比較する。入力に対する出力と期待する出力に差分が出ればテストはバグを発見したことになる。

発見したバグを修正する作業をデバッグというが、ここでは詳細に立ち入らない。

修正作業でバグを埋め込まない方法

ソフトウェアを修正すると残念な事にバグを埋め込むことが多い。非常に多い。経験豊富なプログラマはそれを知っているが、経験が浅いプログラマは根拠なく自分は大丈夫だと信じている。残念な事に痛い目にあってもその信念はなかなか揺らがない。

バグを埋め込まない方法というのは残念ながら知られていないので、修正した後、あれやこれや確認をしてみて、祈るというのが業界標準になっているらしい。

そこで、テストプログラムだ。テストプログラムがあれば、少なくともテストプログラムが確認することに関しては確実に動作が保証できる。もちろんテストプログラムがざるであれば、それ以外のところでぼろぼろ抜けができるが、それは別のはなしである。

テストプログラムによって、埋め込んだバグをすぐに発見する。発見するのは、早ければ早いほど修正は簡単だ。一つ修正して、テストを流して、それで不具合が発見されたとしたら、その修正がなんらかの影響を与えているというのはすぐにわかる。サルにでもわかる。どこを修正したのか知っているので、簡単にバグを直せる。1週間後に発見したとしたら、どこを修正したのか、その変更箇所を探すだけでも簡単な作業ではない。出荷後に発見されたらもっとバグ修正は難しくなる。

修正作業でバグを埋め込まない方法は、バグを埋め込んだらすぐに発見することだ。修正する度にテストプログラムを流してテストをするということだ。そしてバグを発見したらすぐに直すということである。

テストプログラムを作ると言うことは、エクセルのシートにテストケースを書くことではない。

テストをしているかとプログラマに聞くと、もちろんしていると答える。テストプログラムを書いているかと聞くと、言葉を濁す。

テストをするには、ソフトウェアを実行して、その動作を目で確認する手動の方法と、なんらかのテストプログラムを書いて、そのテストプログラムによってソフトウェアの動作を確認する方法とある。

前者と後者には雲泥の差がある。

そして、このレガシーコード改善ガイドがいう、編集して祈るは前者のアプローチで、保護して変更するは後者のアプローチである。テストのないコードというのは前者のことで、それはレガシーコードだと言いきっているのである。いつコードを書いたかが問題ではない。テストプログラムがあるかないかがレガシーかそうでないかを分けるという強烈なメッセージなのである。

いまそこにあるレガシーコード

新規にソフトウェアを作るときに、未だにテストプログラムを書かないプログラマがいるが、そのような輩はここでは取り上げない。テストプログラムを書いてねというしかない。テストドリブンで開発してねというしかない。それでもテストプログラムを書かないプログラマに言うことはない。

そうではなくて、誰かが書いたコード、職場の上司かもしれないし、同僚かもしれないし、辞めてしまった誰かかもしれないし、ともかくテストがないコードをメンテするプロフェッショナルであるあなたが、どうやってこのレガシーなコードをメンテナンスするかという議論である。

テストはない。

おそらく文書もないだろう。あったとしても、メンテされていないまったく何の訳にも立たない文書しかないのだろう。

そのコードをどう改善していくか。

結論から言えばテストを書くのである。

テストがないので、変更ができない。変更をするとテストがないので、修正にバグが含まれていないか祈る以外に確認方法がないので、変更はできない。ここに大きなジレンマがある。

単体テストのようなホワイトボックステストで、コードを変更してテストを埋め込むということがテストがないが故にできない。

それでは、どうやってテストを書くのか。それは、外から攻めるのである。前からいくのである。つまり、受け入れテストないしは統合テストと呼ばれている、ブラックボックステストを行うのである。UIのテストであればSeleniumのようなツールを使って動作を確認していくのである。

そして、まわりを固めて徐々に内側に入っていくという戦略になる。

新規に追加する機能であれば、それはテストによって保護していく。徐々にテストが全くない状況からテストがある状況へ進んでいく。

新規開発は内側から、レガシーコードは外側から攻めるという戦略に落ち着く。

テストがアジリティをあげる

レガシーコードは大きな質量を持っている。テストがないので、変更ができない。動いている部分はなるべくそっとしておこうという思慮が働く。そのために、変更に強い抵抗が生じる。アジリティが非常に低い。

それを徐々にリーンな筋肉質な体質に持っていくには、テストを書くしかない。

テストがあれば変更ができる。変化にも対応できる。アジリティも上がる。

鶏と卵である。

テストがあれば、どんどんリファクタリングして、より変更しやすいようにコードを改良できる。コードというのは放っておくとどんどん重たくなっていく。実装を繰り替えすうちに徐々に全体像の理解が進みよりよい実装が見えてくる。そのためにリファクタリングが必要なのであるが、テストがないとそれができない。

テストとアジリティは表裏一体なのである。

読書会が共通の理解を生む

本書を読み、現場のエンジニアと議論をし、どうすればいいかを語った。テストがないコードはレガシーコードだという共通認識を得た。

ここにあるレガシーコードをどうにかしたいという強烈な危機感を共有した。そしてレガシーコード改善の具体的なヒントを本書で得た。このヒントを実践するもしないもわれわれ自身にかかっている。

テストを書こう。その勇気を与えてくれる一冊だ。

あなたが、レガシーコードと戦わなければいけない職場にいるのであれば、同僚とじっくり読書会をすることをお勧めしたい。その効果がすぐにでないとしても、時間がかかっても何かの変化があるはづだ。

価値観を共有する。それが最初の一歩だ。

*1:仕様書のない環境でテストをしていて、どうもよくわからないという動作に出会ったとき、これってバグですかね、それとも仕様ですかねという状況は、そもそも仕様がないのだから分からない、決定しようがない。