翔泳社から出版されている「実践テスト駆動開発」を読みました。
- モックを使うテストのメリットはなんだろうか
- モックを使ったテストを理解するコツはなんだろうか
以前、「単体テストの考え方/使い方」を読んで、モックを積極的に使うロンドン学派の良さが分からないところがもやもやしていました。
実務でもモックを使ったテストを多用しており、レビューする中で読みにくさや今後の保守に対する不安を感じる場面が度々ありました。
総じてテストでモックを使うことに対して、まだまだ理解できていないところが多いと感じ、ロンドン学派のバイブルと呼ばれている「実践テスト駆動開発」を読むことにしました。
この記事では、モックを使ったテストのメリット、「実践テスト駆動開発」を読んで得たテストのノウハウをお伝えします。
モックを使ってオブジェクト間のやりとりを表現する
この書籍には、開発後の保守の観点からモックを使うメリットを得ることを期待していました。
読み始めると、いたって自然にモックが登場してどんどん使われるように。
「あれ…?思ってたのとなんか違う…」と思いつつも、モックを活用したテストから次々に設計の改善点が見つかります。
読み終えた今だからわかることですが、この本はモックの良さを伝えるのではなく、モックを活用したテスト駆動開発により、より良い設計に自然と向かっていくさまを紹介する本でした。
第Ⅲ部では実際にテスト駆動開発でアプリケーションを開発していくサンプルが紹介されています。
随所に散りばめられたインクリメンタルな設計改善は「モックを使うメリットはここにあり」と言わんばかりです。
「第28章 ティム・マキノンによるモックオブジェクトの簡単な歴史」の最後にも以下のように書かれています。
ジョーにはまた、モックオブジェクトを使ってオブジェクト間のインターフェイスの設計を進めるという考え方も見えていた。あの頃の私たちは、モックオブジェクトを設計ツールとして使うアイデアを広めようと必死だった。
明確に「モックを使用するメリットはこれ!」とは書かれていませんが、モックを使ってオブジェクト間のやり取りを表現し、コードで設計を表すことで設計の改善点を見つけ出すというのがモックのメリットと捉えました。
よくよく見てみると、原著のタイトルも「Growing Object-Oriented Software, Guided By Tests」なのであながち間違っていないと思いいます。
(実践テスト駆動開発ってかなり尖った意訳な気が…)
レッド・レポート・グリーン・リファクタリングの4フェーズ
テスト駆動開発の書籍ということだけあって、テスト駆動開発の進め方についても学びがありました。
「テスト駆動開発」を読んで、テスト駆動開発とは「レッド→グリーン→リファクタリング」の3つのフェーズを繰り返しながら開発を進める開発手法と認識していました。
しかし、この「実践テスト駆動開発」ではレッドとグリーンの間に「レポート」を挟んでいました。
この「レポート」とは何かというと、レッドで生じたエラーメッセージを改善するというもの。
エラーメッセージを理解しやすくすることで保守性を高める意図がある。
手間をかけて役に立つ診断メッセージが出力されるようにすることで、そのテストが何をすべきか、さらにそこから、そのコードが何をすべきかが明らかになる。
このように書かれており、これまでそのような視点を持ったことがなかったことに気付かされました。
エクスペクテーションとアローアンスに意図を込める
アローアンスという言葉を聞いたことがない方もいらっしゃるでしょう。
自分もそうでした。
エクスペクテーションは意図したとおりにマッチすることを表現するものですが、アローアンスはマッチしてもしなくても良いというものです。
アローアンスの用途は主に2つ。
- 別のテストケースでカバーしているため、現在のテストケースでは確認の必要がないものを明示する
- スタブのような副作用のない呼び出し処理に値を返させる
これまでは、どちらもエクスペクテーションとして表現していましたが、これがモックの理解しにくさにつながっているのかもと考えさせられました。
エクスペクテーションとアローアンスを使い分けることで、現在のテストに対する重要性を明示し可読性を高められるかもしれません。
テストデータの生成にビルダーを活用する
第Ⅳ部・第Ⅴ部はテストのノウハウが記載されていました。
中でも「第22章 複雑なテストデータの構築」はなるほど、と感心した部分だったのでご紹介します。
ドメインモデルや値オブジェクトを使ってプロダクションコードを実装していると、どうしてもテストデータの作成が重くなりがちです。
なんのために、なんのデータを作成しているのか分からず、可読性が低いなと感じることがたびたびありました。
本書ではビルダーパターンを活用してテストデータを作る方法を推奨していました。
オーバーロードの機能を持つ言語であれば、一層わかりやすくテストデータを作成することができます。
例として注文のテストデータを作成するケースが紹介されています。
Order order = anOrder().from(
aCustomer().with(
anAddress().withNoPostcode()
)
).build()
着目しなくていいパラメータはビルダーのデフォルト値として設定しておき、テストケース特有の値を設定する形で、何に着目すればよいかが一目瞭然で良さそうでした。
「第Ⅲ部 動くサンプル」を読み返す
一通り書籍を読み終えたわけですが、理解度としては4割程度かと思っています。
というのも、第Ⅲ部で説明されていたサンプルプロジェクトや周辺システムに対する理解が浅く、それらに対する疑問によって頭に入っていない部分があるからです。
結果、モックを使用する意図や背景を見落としている可能性があると感じており、読み返しは必須かと考えています。
モックを使う目的のひとつが設計の改善ということも分かったので、再読する際はクラス図などを用意しながら読むことでモックに対する理解を深めていきたいと思います。
テストに意図を込める、テストを読んだ人に伝える
モックを使ったテストはエクスペクテーションを通じて、関連するオブジェクトに何を期待するかを示すことができます。
普段使っているPHPUnitにはアローアンスの機能はなさそうですが、エクスペクテーションに呼び出し回数の指定をするかどうかや、オリジナルのアサーションを作ったりなど、本書で紹介されていたテストに意図を込めるというノウハウは使えそうです。
これまでは、メソッドなどが意図した挙動をするかどうかという点だけに着目していました。
今後はテストで何を伝えたいか、テストが落ちたときのメッセージは意図を伝えられるかといったところまで考慮してテストを作っていきたいです。
複雑なテストデータの生成にはビルダーを活用する
プロダクションコードではビルダーパターンを使っているケースもありはしたのですが、テストでビルダーパターンを使ったことはありませんでした。
テストデータの準備については、無駄に長くなってしまってテストが読みにくいと感じたことが過去ありました。
ビルダーパターンをテストケースの作成に用いるというアイデア自体を持ち合わせていなかったので、本書で紹介されていた方法を実践してみたいと思います。
この記事では「実践テスト駆動開発」を読んで得た、モックを活用したテスト駆動開発の一部を紹介しました。
しっかり理解できたかと言うとまだまだのレベルです。
しかし、モックを使うことでオブジェクト間のやり取りを表現でき、そこから設計に対する改善の示唆が得られることがわかりました。
古典学派のテストに対する考え方はプロダクションコードの将来的な保守を見据えた「守りのテスト」、ロンドン学派のテストに対する考え方はテストを活用して設計を継続的に改善していく「攻めのテスト」という風に理解しました。
自分としては、テストは成長していくプロダクションコードの安全を守るために存在していてほしいので、古典学派の考え方でテストを作っていくのが良さそうに感じました。
一方で、テストを活用して設計を改善していくという取り組みも捨てがたく、これはこれで代えがたい良さがあると考えています。
さすがに実務で両方のテストを用意しながら開発するのは気が引けるので、個人開発で両テストを作って開発していくような取り組みを試してみたいと思います。
「実践テスト駆動開発」について、まだまだ紹介しきれていない部分も多いです。
おすすめの本ですので、ぜひ手にとって読んでみてください!