TDD/BDDの思想とテスティングフレームワークの関係を整理しよう:いまさら聞けないTDD/BDD超入門(2)(1/3 ページ)
TDD/BDDの思想に触れ、フレームワークとしてxUnit、JBehave、xSpec、Cucumber、Turnip、TestDoxを紹介する。
前回の「テスト駆動開発/振る舞い駆動開発を始めるための基礎知識」でも紹介があったように、さまざまなテスティングフレームワークがあります。例えばTDD自体は、Kent Beck(ケント・ベック)氏が著書『テスト駆動開発入門』(ピアソンエデュケーション刊)の中で述べているように、「分析技法および設計技法であり、実際には開発全てのアクティビティを構造化するための技法」です。
TDD(テスト駆動開発)/BDD(振る舞い駆動開発)を実践することと、特定テスティングフレームワークを採用したり開発したりすることを分けて考えておかないと、TDDすることが目的になってしまったり、あるテスティングフレームワークを使うことが目的になってしまったりする場合があります。
本記事では、TDD/BDDと各種テスティングフレームワークの関係を整理していきます。
前回はTDDの概要と進め方、目的と効果、歴史、さまざまな手法への展開、課題に加え、BDDの概要と種類、重視される考え方などを解説しました。具体的には、次の言葉を解説しました。TDDのステップであるRED/GREEN/REFACTOR、インサイドアウト、アウトサイドイン、ATDD、BDD、Tests as Document、Specification By Exampleなどです。ここからは、これらの言葉を使って解説します。
「TDD/BDDフレームワーク」という言葉
幾つかの書籍、OSS、ブログでは特定のテスティングフレームワークを指して「TDDフレームワーク」「BDDフレームワーク」としています。このような例は他のツールでも見られ、例えば特定のタスク管理ツールを指して「アジャイルツール」と書かれていることがあります。
それ自体に特別な意図があるとは思えませんが、(例えば)「カンバン」を使ったからといって、その組織がアジャイルであるかどうかが決まるわけではないのと同じように、あるテスティングフレームワークを使ったからといって、それがTDD/BDDであるかは別です。明示的に書くと「TDDやBDDをサポートするテスティングフレームワーク」くらいなものでしょう。
これを勘違いすると、「TDDフレームワークを使えば、TDDができるようになる」「BDDフレームワークを使っているから、テストコードが仕様書といえる」「TDDフレームワークを導入すれば、品質が上がる」「TDDフレームワークを使っているのにTDDができないのはおかしい」などという主張が生まれます。
ここからは、思想としてのTDD/BDD、ツールとしてのTDD/BDDに分けて引用を交えて説明します。
思想としてのTDD/BDD
TDD/BDDの概要に関しては前回で扱いました。簡単にまとめると、次のようにいえます。
- TDDはRED/GREEN/REFACTORを回す
- テストファーストである
- 実装対象のユーザーの立場でテストする
ユーザーは、これから実装しようとしているものを使う人です。あるモジュールやクラスを実装しようとしているときは、そのモジュールやクラスを使う人が、ユーザーです。もっと大きくなって、エンドユーザーから見える機能になれば、エンドユーザー(やプロダクトオーナー)がユーザーです。ユーザーは実装対象によって変わります。
これに加えてTDD、BDDそれぞれで次のようなことがいわれています。
TDD
TDDを世に広めるキッカケを作ったKent Beck氏は、著書『テスト駆動開発入門』の中で次のように語っています。
自動テストが失敗した場合だけ、新しいコードを書く。重複を取り除く。2つの規則はプログラミングのタスクにおける順番を意味する。
- レッド:動作しないテストを少しだけ作成する。おそらく最初はコンパイルできない
- グリーン:テストをすぐに動作させる。そのためには、どのようなコードでもよい
- リファクタリング:テストを動作させるためだけに作成された重複を全て取り除く
またTDDのエキスパートとして知られるRobert.C.Martin氏は、著書『Clean Code』(アスキー・メディアワークス刊)で次のような原則を挙げています。
3つの原則を守りながら実装を進める。
- 失敗するテストができるまでプロダクトを書いてはいけない
- 失敗するテストがある場合にはそれ以上テストを追加してはいけない
- テストを成功させるプロダクトがある場合にはそれ以上プロダクトを追加してはいけない
加えてユニットテストは次のようであるべきとも語っていて、5つのポイントを頭文字を取って「F.I.R.S.T.」と呼んでいます。
- Fast(高速である):テストは高速であること
- Independent(独立している):テストはお互いに関連すべきではない。テストは好きな順序で実行できるべき
- Repeatable(再現性がある):テストはどんな環境でも再現可能であるべき
- Self-Validating(自己検証可能):テストは、2つの結果、成功か失敗かのどちらかを出力するべき
- Timely(適時性がある):テストは必要なときにすぐ書けるべき
また、TDDにおけるテストコードは説明的で読みやすい文章であるべきです。例えば、対象のメソッドに対する説明ではなく、「対象がどのような変化を生むのか」を書くべきです。そのためにユーティリティ的なメソッドを書くこともあるでしょう。テストコードがあまりにもメソッドのドキュメントになっている場合は、設計が曖昧である可能性が高いです。
BDD
TDDを進めていく中で、「何をテストすべきか」「テストとは何か」といった問いに対しての説明として「Test」ではなく「Behavior」という言葉を使って説明する方が開発者にとって理解しやすかった、というDan North氏の経験からBDDは生まれました。
根本的にTDDとBDDは違わず、それまでのTDDの語彙では伝わりにくかった部分を言い換えたり、テンプレートを与えることで幾分かハードルを下げるという役割を持っています。
例えばTDDでは、テストコードについて実際のテンプレート的な指針は示されておらず、説明的で読みやすい文章にするかどうかは、プログラマーの裁量に任されていました(読みやすいテストメソッドであることをサポートするツールとしては「AgileDox」「TestDox」(後述)というツールが存在する)。
そこでBDDでは、次のような語彙を導入します。
- 「Given xxx. When yyy. Then zzz.」:テストコードを読みやすくするテンプレート。「xxxであるときに、yyyすると、zzzになる」というように手順的にして読みやすくする
- 「Given」:事前状態を表現する
- 「When」:刺激や操作を表現する
- 「Then」:事後状態を表現する
- 「x should y: テスト自体を「対象(x)がどうである(y)べきか?」と表現する
BDDの実質的な開祖であるDan North氏は自身のブログ(日本語訳)で次のような表現を残しています。
表現力のあるテスト名は失敗したときに役に立つ。
- バグを埋め込んでしまった。悪い子だ。解決法:バグを直す
- 意図された振る舞いは変わらず関連性があったが、どこか別の場所に移されていた。解決法:テストを移動し、場合によっては変更する
- 振る舞いがもはや正しくない。システムの前提が変わってしまっている。解決法:テストを削除する
「Given」「When」「Then」という語彙の称えるべき点の1つは、実質的にFSM(有限状態機械の略。状態遷移図を思い浮かべてください)のDSLであり、FSMとして完成している文章にすることも、数パターンしか書かないこともできる、ということです。BDDがFSMのDSLを提供することで、幾人かには読みづらいと感じられるソフトウェア仕様の合意のしやすさ、保守性向上、書きやすさに貢献しているといっていいでしょう。
思想のまとめ
BDDはTDDの表現方法やプロセスを特化させた方法論であり、相入れないものや根本的に異なるものではありません。TDD/BDDとは「より良い開発をプログラミングによって支えるために、上のようなプロセスやガイドをベースにソフトウェア開発をすること」と言い換えてもいいでしょう。
Copyright © ITmedia, Inc. All Rights Reserved.