1. おそらくこういう理由
1-1. すぐ検証
1-2. 強制的な自己レビュー
1-3. ミスが確実に減る
1-4. すぐ実行・小さく実行
1-5. テスタビリティの考慮
1-6. 回帰テストしやすい
1-7. 追加がラク
1-1. すぐ検証
ソフトウェア開発は、コーディング(実装や製造とも言われます)の後、単体試験、結合試験、総合試験(呼び方はプロジェクトによって異なりますが)と、検証する工程が続いていきますが、後になるほど、
- 試験項目が抽象的になり、コードに起因するバグを見つけにくくなる
- バグを見つけた場合、切り分けと修正に時間がかかる
となります。テストコードを書くということは、一番早期の時点でバグの除去を行うことができます。
1-2. 強制的な自己レビュー
テストコードは、対象となるコードをよく見ないと書けません。すなわち、自己コードレビューをせざるを得なくなります。
目視でのコードレビューは、特に他人が見た場合、細かい誤りを見つけられないことが「多々」あります。そして、往々にして「レビュー」にあまり時間を取れないことが多いです。
何より、書いたコードを一番わかっているのは書いた本人であることです。
1-3. ミスが確実に減る
ケアレスミス、論理判定ミスなど、細かいミスは確実に減ります。
最近の私事ですが、テストコードを書いたモジュールと書いてないモジュールとで、コード行数あたりのバグ数が10倍以上は差がありました(前者はそもそもミスに起因したバグがなかったのですが)。
忙しい時ほど、ミスはしやすいですが、ユニットテストも省略しがちです。しかし、後の工程でアホみたいにバグが検出されると、ユニットテストをさぼった以上に時間が取られます。
「急がば廻れ」です。
1-4. すぐ実行・小さく実行
最近の言語(Java、Pythonなど)は、特定のメソッドやクラスを直接呼び出すことができ、従って、ユニットテストも対象となるメソッドやクラスを直接呼び出すことができるようになっています(例:Junitなど)。つまり、作ったらすぐ実行して確認できます。
CやC++では、テストしたい関数を直接呼ぶ仕組みを仕込まないといけません(後日、その方法を書きます)。
しかし、Junitなどが普及していなければ、CやC++のコードでユニットテストを導入しようと思わなかったかもしれません。
1-5. テスタビリティの考慮
最初からユニットテストをする前提でコードを書くと、テストしやすいコードになります。
テストしやすいコードは、たいてい、可読性や保守性が容易となります。それは、関数やメソッドの長さや複雑さが適切であったり、処理の凝集性が高かったり、外部依存が少なかったりするからです。
そして、バグを修正するときにも、ユニットテストで確実に検証して後の検証工程に回すことができます。
1-6. 回帰テストしやすい
機能追加を行った場合、既存部分が正しく動作するか確認したいことがあります(回帰テスト)。以前は想定していなかった呼び方をして新たなバグを入れ込んでいたり(または見つかったり)、動作環境が変わったりして実は動作しなくなっていることも、起こりえます。
そんな時に、テストコードを残しておくと、回帰テストを実行しやすくなります。
ただ、既存のテストコードのインターフェースが合わない(例えば「引数が合わない」など)で、ビルドができなくなることがたまに起きますが、テストコードをコメントアウト、最終的に放棄されてしまうことが よくあります。
1-7. 追加がラク
テストが足りなければテストコードを追加して再実行すればよいのです、と当たり前のことを書いていますが、普通の項目テストで項目を追加して再実行するよりははるかに楽です。
2. テストコードを書く上で気になること
2-1. テストコードも保守性を考慮する
2-2. テストコードをレビューする?
2-3. このテスト、意味あるの?
2-4. 開発用ツールでもテストコードを書こう
2-1. テストコードも保守性を考慮する
使い捨てを前提としたならば保守性はどうでも良いのですが、せっかく作ったのに都度捨ててしまうのはもったいないです。保守性といっても以下の点を考慮すればよいかと思います。
- テスト関数の行数を小さく
- 共通的な処理はまとめる
あと、assertだけだとテスト目的がわかりにくいので、目的や試験項目(条件・確認事項など)のようなコメントを追加することをします。
2-2. テストコードをレビューする?
以下の観点でレビューすることがあります。
- 条件分岐のこまごましたところまでテストできているか(限界値判定なども含む)
- テストコードによって、対象コード(メソッド・クラス)をどう使う前提なのかが分かるようになっているか
- 外部リソース(ファイルなど)に依存する場合、適切なテストデータを生成できているか
プロジェクトの性質次第かなと思います。
2-3. このテスト、意味あるの?
例えば、構造体やクラスのメンバーに代入するだけの処理は、コードを「見たら」バグがあるか分かるはずと思うのですが、人間、意外なところでミスをします。
そこで自己コードレビューを兼ねてユニットテストを行うのです。
「バグなんかあるわけがない」、という思い込みを捨てることが大事です。
もちろん、テストコードを書いてもすり抜ける可能性がゼロではありませんが、レビューをただやるよりも誤りに気付く可能性は高くなります。
2-4. 開発用ツールでもテストコードを書こう
例えば、大量にテストデータを生成するツールを作った場合、そのデータに誤りがあると、やり直しをする必要があったりします。しかも、大量生成の場合、数時間かかるとかザラにあります。昔、何度もやり直しすることがあって(しかも数日無駄にした)、うんざりさせてしまった経験があるため、そういうツールでもテストコードを書くようにしています。
おまけ
私は「コードは動かしてなんぼ」というポリシーを持って開発しています。ユニットテストで対象コードを実行しやすくしたり、カバレッジで100%目標にしたりというのは、そういうポリシーと合致するのです。動かせば動かすほどバグは見つけやすくなりますし、動かさないコードに意味はありません。
あと、この記事の内容、抽象的に書いたので、具体的な事はどこかで。