最近テスト駆動開発(Test-Driven Development: TDD)を試している。
テスト駆動開発とはソフトウェア開発の1つである。これの提唱者であるKent Beckの本を今読んでいるが、かなり面白い。
通常の開発では次のように進む。まず仕様を決定する。次に実装する。そして最後にテストする。
対して、TDDは仕様が決まった後にまずテストを作成する。そのあとに実装する。
TDDの方法は有名であり、それは次のループである。
- レッド
- グリーン
- リファクタリング
TDDはテストコードとプロダクトコード(実装コード)を行ったり来たりする。テスト(単体テスト)は1つずつ書いて、追加したテストケースがエラーになったとき初めて修正する。一旦テストを通るコードを書いたら、最後に綺麗に整える。これがTDDの概要である。
テスト駆動開発はその名の通り、テストを中心に開発するものである。Beck自身ははじめこの方法を「テスト・ファースト・プログラミング」と呼んでいたが、この方法がテストを優先するという意味合いである「テスト・ファースト」以上の何かを意図しているので、テスト駆動開発という名称になった(p.278)。
ここ最近TDD的な開発をしている。きっかけは次のような経緯である。
1つはかなり複雑な修正をしなければならず、単体テストをちゃんと書かなければならないが、これまでの開発だとテストを蔑ろにしていたからである。「テスト書くのは大事だよ。でももう実装したし...。テストは...あとでいいっか」この態度をずっとしていた。それなら初めからテストを書きながら開発した方がいいのではないかとTDDを試した。
もう1つはTDD信者の影響である。もともとTDD自体は知っていたが、「私はテスト駆動開発信者なので」と言われている人がいたので、さらに興味を持った。「今この本を読んでいます」と言ったら「いいですよね! この本」と喜んでくれた。「何か他にテスト駆動開発を理解するためにおすすめの本はありますか?」と聞いたら「テスト駆動を知りたいなら、とりあえずこの本をちゃんと読めば十分です」と言っていた。まだちゃんと読んでいないので(4割ぐらい)、今回は書評はできないが、ちゃんと読もうと思う。
テスト駆動(的な)開発を実際にした現在の感想はだいたい次のとおりである。
- TDDを身につける(Mastering TDD)ことはとても難しい。「あなたが平均的なスキルのプログラマで、プレッシャーに屈して手っ取り早い方法を選択してしまうような人間であってもだ。テスト駆動開発はどんなプログラマでも使えるテクニックの集まりであり...」(p.xix) とはあるが、それでもTDDをマスターするには経験やセンスが必要だと思う。
- プロダクトコードを修正するだけでなく、テストコードも適宜修正しなければならない。「プロダクトコードが修正されたから、このテストは不要だな。削除しよう」と判断しなければならない。プロダクトコードとテストコードの行ったり来たりのダイナミックな開発は慣れる必要がある。
- さらにリファクタリングも難しい。「テストを通るぐちゃぐちゃなコードは書けるかもしれないけれども、そこから綺麗なコードに書き直す(リファクタリング)は一体どうやればいいのか」と思ってしまう。1人でのマスターは難しく、ペアプログラミングを通じて直接教えてもらう必要があると思う。もっともこれは「knowing thatとknowing howの違いであり、理解することは簡単だが実践することは難しい」ということに過ぎないのであるが。
- TDDは結構好き。「テストを追加してエラーが生じない限り、コードを修正しない」という考えは特にいい。
- TDDの要点は「仕様をテストの形で表す」ということであるが、このようにテストという形で仕様を残せば、将来この機能がどういうものかがわかるのでいいと思った。
- これまではPostmanを使って動作を確認しながら、実装していたが代わりにテストを使って開発するようになった。Postmanでの実装ならば、基本的に成功の場合のみ結果が残り、エラーの場合どういう結果なのかなどを残すことができなかった(少なくともそうするためにはドキュメントを書かなければならない)。対して、テストを使いながらの開発ならばエラー結果も含めてコードに残るからいいと思った。
- テストコードが大量になってしまう。ファクトリのようなテスト用データを作成する機能も追加しなければならないと思った。
- 大量のテストケースを書いているので、デプロイ時間が大幅に増えた。
- テスト自体が間違っている可能性が十分にある。特に大量にテストをコピペするので、テストが誤っていたりテストが実はテストされていないということがありうる。
TDDをしているときに、テスト駆動的な考えは単にソフトウェア開発に留まらないのではないかと思った。哲学としてのTDDである。以下はそのアイディアである。
- 従来の哲学や社会理論は理論(仕様)から実践(実装)という流れである。ほとんど検証(テスト)はされることがないが、あったとしても最後である。対してTDDとしての社会理論は理論を構築した後に、まずテストを作成する。そしてすべて失敗することを確認する。それから実践する。
- 新しいテストが追加されても現在の社会や制度がパスするならば、それらを変更する必要はない。
- ある考えをテスト形式として表すことができないのならば、それは理解不能であり、もはやそれに議論する意味はない。
- 「あなたの言っていることを理解しているかどうか」を判定する方法は「あなたの言っていることから私が作成したテスト形式が、あなたが思っているテスト形式と一致しているかどうか」である。
- あなたの言っていることや考えから私がAというテストを考えたとする。そしてテストAをあなたに知らせる。私が「あなたの考えや言説から私はAというテストを考えたのですが、これで間違いないでしょうか?」と聞く。
- もしもあなたが「そのとおりです」と言うならば、私はあなたの考えを理解しているということである。
- もしもあなたが「違います」と言うならば、私はあなたの考えを理解していないということである。
- もしもあなたが「私の考えはあらゆる意味でテストのようなもので表すことはできません」という言うならば、もはや私はあなたのいかなる考えを理解することはできないということである。
- 2つの考え(思想・哲学)があるとする。それぞれの考えからそれぞれテストが作成されたとする。
- もしもテスト結果が両方同じならば、その考えがたとえどんなに違うものと思えるものであろうとも、両者は同じである。
- 考えが異なるとはテスト結果が異なるということである。
- テストを追加することで2つの考えが異なることはありうる。ある2つの考えにおいて、3つのテストではすべて一致しているが、追加されたテストで結果が異なるということはありうる。その場合は、「3つのテストの意味で両者は同じである」ということである。または「3つのテストの観点で両者は同じである」ということである。
色々な意味でテスト駆動開発をこれからも勉強したい。
僕から以上