いまさらだけど、家でもテスト駆動するようになった
前書き
いまどきテスト駆動開発の話をするというのも、まるでこの時期(5月末)に桜の話でもするようで、少し気後れするところもある。BDDとか言っておけば、少しは気も紛れるだろうか。まぁ、いいや。季節外れもまた良し。
今年に入った頃からようやく、家でちょっとしたコードを書く時でも、テストファーストを取り入れるのが習慣になってきた。
その中で、いろいろ実感として頭に溜まっているものもあるような気がするので、今日はその辺のことをアウトプットしてみようと思う。
基礎知識
一応書いておくと、TDD = Test Driven Development = テスト駆動開発は、コーディングをする時は先にテストコードを書きましょうっていう手法。
自分はXP関連の書籍で軽く説明を読んだり、Webでそれっぽい記事を読んだりしたくらいの知識しかないので、正直あまり詳しくはない。だから自分がやってる方法が、正しくテスト駆動だとも、その親戚の振舞駆動だとも感じていない。
ただ、テストフレームワークを使って先にテストを書くことで「コーディングが楽になった」、「コードの質が上がった」、「身長が3cm伸びた」といったようなメリットは享受できてると思う。
自分なりにテストコードを役立てることはできているわけだ。
家で書くコードについて
家で書くコードというのは、それほど完全性を求められるものでもない。自分の目的に沿って動いてくれれば、多少バグがあっても構わない。
仕事でテストコードを書く現場を経験したことは何度かあった。そしてそれは品質やエビデンスなどの面でメリットがあることだと感じていた。ただ、少し杓子定規なところがあって面倒だとも思っていたりした。
仕事で書くちゃんとしたコードではなく、家で書くいい加減なコードにもテストコードを付加するというのは、義務感とか主義とかじゃなく、テストコードを書いた方が楽だと実感していないとなかなかできない。
サンプルコードとかワンライナーのためにテストコードは書くなんてことは、さすがに今でもやってない。
だいたい50行以上のコードになると、テスト駆動を選択することが多い気がする。ということは、自分の頭の中では50行のコードでもテスト駆動の方が楽だと判断されていることになる。
目視でやることをコードに任せるという意識
自分が家で書いてるテストコードはかなりいい加減なものだ。最低限のチェックをさらっと書いて、それで終了。これは普段、目視で確認していることをテストコードに任せるイメージに近い。
ちょっとしたモジュールを書いたら、ちゃんと動いているか確認するために、デバッガとか標準出力を使って実行結果をさらっと目視で確認することがあると思う。
それをコードを使って確認すれば、テスト駆動らしき何かになる。肩肘張ってちゃんとテストしようとは考えずに、自分が疲れない程度に、思いついたことだけチェックする。
最近私が使ってるSpecsだと、かなり自然言語に近い書き方ができる。こんな感じで。
"渡した文字列の文字数が返る" in {
func1("0123456789") must be equalTo(10)
}
最近のTDD(上の例だとBDDか)用のフレームワークは記述が楽なものが増えているので、書くのにそれほど時間は取られない。とはいえ、目で1度きり確認するのと比べると、それなりに面倒ではある。
それでも長期的に見れば、その面倒さを覆すだけのメリットがテスト駆動にはある。
テスト駆動のメリット
自分が実感してるテスト駆動の良いところは、この辺。
1. 1回確認したことは再度確認しなくてもいい
目視での確認だと、勘違いだったり、夜中に妖精さんが見せた幻だったりすることが良くあって、結局もう1度確認し直すという手間が発生することが多い。
特に金曜や土曜の夜に家で「朝まで1人プログラミング大会〜」などと盛り上がって書いたコードは、かなり危うい状態になる。
テストコードを書いて置けば、どんな状態で書いたコードであっても、このテストに対してグリーンが出たという証明を残すことができる。
2. 信頼性を積み上げていける
プログラムを動かしていてちょっとしたバグを見つけたら、すぐにそのバグをチェックするコードを追加する。
そうやって徐々に項目を追加していくと、テストコードがだんだんと質が良いものになっていく。目視のテストはコードを修正するたびに崩れて無に帰るけど、TDDではテストを書けば書いただけ積み上がっていく。
もちろん仕様の変更でテストコードが無意味になることもあるけど、適切な場所にテストを書いておくと、意外と潰れるコードというのは少なかったりする。
テストコードが徐々に成長していると感じられることは、やりがいに繋がったりもする。
3. 気になる点を事前に確認できる
プログラムを書いていると、「ここの挙動、危ういかも」という気持ちがよぎることがけっこうある。
テスト駆動では、危ういと思った瞬間に確認用コードを追加するのが一般的。そういう開発手法だし。
それに対してテスト駆動してない場合は、後でテストする時に確認しようという気持ちになることが多いように思う。実装とテストはフェーズとして切り分けられているので。
危ないところをその場でチェックしようという気持ちをプログラマに起こさせてくれるところは、テスト駆動の1つのメリットだと思う。
4. テストしやすいコードを書くようになる
テスト駆動では先にテストを書くわけなので、テストしやすいコードを書いた方が、プログラマ的に楽になる。
テストがしやすいコードというのは、つまり副作用が少なくて振舞が明快なコードであり、バグが潜伏しづらいコードでもある。
そういうコードを書くモチベーションとか視点を与えてくれるという点で、テスト駆動は便利だと思う。個人的にはこれがテスト駆動の一番のメリットだと思っていたりもする。
5. 再利用する気持ちが高まる
1から新しくコードを書く場合は、1からテストも書かないといけない。つまり、新しくコードを書くことに対するハードルが少し高くなる。
既存のコードは既にテストを書いてあるので、取り入れる際のハードルが少し低くなる。
その結果、新しくコードを書くより、極力既存のコードを活かそうという気持ちが高まる、ような気がする。
6. リファクタリングが楽
慣れないうちは微妙な場所にテストコードを書いてしまって、リファクタリングするたびにテストコードも直すという情けないことをしていた記憶があるけど、慣れてくると確かにやりやすくなった。
特に「ちょっとここの言い回しを変えてみよう」っていう遊び心が出た時に、コード変えてみて、テスト流してみて、意外な結果が返ってきたりすると「あー、こうなったか」と楽しい気持ちになったりする。
メリットのまとめ
まとめると、テスト駆動の自分的なメリットは以下の3つに集約できる。
- 綺麗なコードを書ける
- コードの信頼感を積み上げることができる
- 修正を加えても信頼感が崩れにくい
もちろん、テスト駆動と相性が悪いコードってのはある。そういうところは無理して当てはめようとせずに、「こりゃ、ダメだ」と言って別のやり方に切り替えればいい。
勘違いされやすい点として、テスト駆動っていうのはテストを肩代わりしてくれるものという意識があったりする。けど、真面目にTDDしたからといって現場で良くやる単体とか結合テストの項目数を減らせるとは思えない。
どっちかというと、バグを修正する時のデグレの危険性が少し軽減されたり、バグの検出数を減らせたりといった効果の方が大きいんじゃないだろうか。
そういう意味では、Test Firstって言葉はけっこう誤解を招きやすくて、最初からBDDでSpec Firstとか呼ばれていれば良かったんでないかとも思ったりする。
個人的にはテストコードは牧羊犬のようなイメージがある。1匹1匹の羊に対して「この場所にいろ」とは言えない。けど、群れから離れようとした羊を注意することはできるし、異変が起きた時に知らせてもくれる。居てくれると何かと心強い。
後書き
プログラムっていうのは積み木に似てる。
最初のうちはブロックの数も少なくて、何も考えずに積み上げていける。でも、途中から積み上げるのがだんだん難しくなってくる。
一般的なコードは機能を積み上げる。でも、テストコードは安定性を積み上げる。
テストコードで安定性を得た機能は、積み上げる時に感じる危うさが減る。時間が経って「そういえば昔、こんなもの書いたよなぁ」と思ったコードを引っ張り出した時でも、躊躇なく積み木の中に取り入れることができる。
その辺の安心感というか、自分が書いたコードを使い回す時に感じる気楽さが、自分の中で「テストコードを書いた方が楽だ」という感覚を生み出しているのかなぁ思う。