TDD/BDDの思想とテスティングフレームワークの関係を整理しよう:いまさら聞けないTDD/BDD超入門(2)(3/3 ページ)
TDD/BDDの思想に触れ、フレームワークとしてxUnit、JBehave、xSpec、Cucumber、Turnip、TestDoxを紹介する。
xSpec
BDDフレームワークと分類されることが多く、前述のJBehaveからインスパイアされて開発された、Rubyの「RSpec」を始祖とするテスティングフレームワークの総称です。「SpecBDD」と呼ばれることも多く、JBehaveと比較してよりコードの見やすさやドキュメンテーションの作りやすさに注力していることが挙げられます。
RSpecでは、xUnitでの単語を次のように置き換えています。
- 「Fixture」→「Context」
- 「Test」→「Spec」
RSpecと、Groovy製のBDDフレームワーク「Spock」の簡単な例を次に示します。まずはRSpecです。
describe FizzBuzz do [3,99].each do |num| it "return 'Fizz' for multiples of 3(#{num})" do expect(FizzBuzz.says(num)).to eq("Fizz") end end [5,100].each do |num| it "return 'Buzz' for multiples of 5(#{num})" do expect(FizzBuzz.says(num)).to eq("Buzz") end end [15,90].each do |num| it "return 'FizzBuzz' for multiples of 15(#{num})" do expect(FizzBuzz.says(num)).to eq("FizzBuzz") end end [1,98].each do |num| it "return number for no multiples of 3 or 5(#{num})" do expect(FizzBuzz.says(num)).to eq(num.to_s) end end end
次がSpockの例です。
class TDDCycleSpec extends Specification { TDDCycle sut def "テストを追加してREDにする"() { given: "プロジェクトを始める" sut = new TDDCycle() when: "テストを追加する" sut.addTest() and:"テストを実行する" sut.runTest() then: "REDになる" sut.status == TDDCycle.RED } def "REDからプロダクトを追加してGREENにする"() { given: "REDになっている" sut = new TDDCycle(TDDCycle.RED) when: "プロダクトを実装する" sut.implement() and:"テストを実行する" sut.runTest() then: "GREENになる" sut.status == TDDCycle.GREEN } def "REDからREFACTORには移れない"() { given: "REDになっている" sut = new TDDCycle(TDDCycle.RED) when: "プロダクトを変更する" sut.refactor() then: "保証外の手順なので認められない" thrown(TDDException) } }
Cucumber
BDDフレームワークと分類されることが多く、xUnitやxSpecと異なってJBehaveでもサポートしている「振る舞いを、フォーマットがある自然言語で書くfeatureファイル」と「実際のテスト実行を、プログラミング言語で書くstepファイル」の2つで1つのテストを構成します(実際には、featureファイルとstepファイルの関係はN:Nになりますが割愛します)。
「Given」「When」「Then」といったBDD由来の語彙を引き継いでいるのはもちろんですが、「Gherkin」というfeatureファイルを書くためのライブラリを導入しているのも特徴です。これによってJBehaveより書きやすくなっています。
xUnit系のように、さまざまなプログラミング言語でその派生が開発されています。また、Gherkinは37の自然言語が用意されています。もちろん日本語もあります。 日本語ではそれぞれ次のような対応になります。
- 「Feature」→「フィーチャ」
- 「Scenario」→「シナリオ」
- 「Given」→「前提」
- 「When」→「もし」
- 「Then」→「ならば」
# language: ja フィーチャ: TDDのサイクルはRED、GREEN、REFACTORからなっています。 GREENからREFACTORを飛ばすことはありますが、REDからGREENを飛ばしてREFACTORしないのが特徴です。 これはテストなどによって保証されている範囲でのみ内部を変更することを「REFACTORING」と呼ぶという定義によるものです。 シナリオ: プロジェクトを始めるときにTDDの最初の一歩になる手順 前提 プロジェクトを始めるときはテストがない もし 要求を満たすテストを追加する かつ テストを実行する ならば テストが失敗して、TDDでいう「RED」になる
Given(/^プロジェクトを始めるときはテストがない$/) do # 処理 end When(/^要求を満たすテストを追加する$/) do # 処理 end And(/^テストを実行する$/) do # 処理 end Then(/^テストが失敗して、TDDでいう「RED」になる$/) do # 処理 end
Turnip
Cucumberは幾つかの技術的なハードルの高さがありました。その1つにfeatureファイルの「Given」「When」「Then」とstepファイルの各メソッドのひも付けが正規表現であることです。注意を払って設計をしなければテストケースが増えたときに対応付けが難しくなったり、正規表現を気にしたfeatureファイルを書くということ自体が難しいと感じる場面もありました。
これを解決するフレームワークとしてTurnipが開発されました。正規表現によるひも付けを廃止し、またstepファイルは前述のRSpecを使います。それでいて、Cucumberで培われた「Gherkin」を使えるようになっています。
TestDox
「キャメルケース」で書かれるテストメソッド名を単語に分割して自然言語として読みやすくするテストリポートのサポートツールです。テスト対象クラスを主語にして、テストメソッド名を単語単位に分割した文をつなげることで「自然言語として読めるか」を気にしながらテストを行う事を可能としています。
統合開発環境「IntelliJ IDEA」のプラグインでもサポートされており、「スネークケース」で書かれているテストメソッド名にも対応しています。
最近の日本ではあまり聞かないツールであるのは、おそらく「テストメソッド名に日本語を使う」選択肢があるという事情によるものかと思います。英語でテストメソッドを書く場合には、使用を検討するといいでしょう。
ツールのまとめ
テスティングフレームワークはxUnitによって大きく前進しました。しかし、TDD初心者がその偉大な習慣を自然と身に付けたり、TDD自体を拡張していくには不足している部分があり、それを補うためのテスティングフレームワークやライブラリが生まれました。
そのような経緯を踏まえても、xUnitがサポートしている機能が少ないからといってBehaviorを表現できないことはなく、xSpecを使っているからといってBehaviorになっているわけではありません。「どのようなツールを使うと、どのようなコードになりやすいか」という話です。そして、ある問題に対して解決しようとした軌跡が、さまざまなテスティングフレームワーク/ライブラリの系譜であるといえるでしょう。
TDD/BDDの誤解を減らして、次回はBDDの振る舞いを詳細に
前回にもありますが、TDDは非常に広範であることがうかがえます。思想とツールに分けてTDD/BDDを見ると、広範なTDDの思想を持っている人が使いやすいツール、広範なTDDの思想をより分かりやすくしたり特化させたりする思想、その思想を形式化したツールといったようなイメージになりました。
テスティングフレームワークはTDDに関する思想の全てを形式知化したものではない(暗黙的な思想が存在する)ですし、用意されているものを使うだけでよいTDDを実践できるわけではありません。そして思想を定着、進化させるために、さまざまなテスティングフレームワークが開発されてきました。
TDDフレームワークという言葉に特別な意味はないのかもしれませんが、「xUnitってTDDフレームワークだからTDDしかできないんでしょう? モダンな開発はBDDだよ。xSpec使おうよ」のような意見によって誤解が生まれやすくなるのは避けたいところです。
次回は一体何を書くことがBDDらしく、つまりは振る舞いを書いていることになるのかを、BDDにおける「振る舞い」を捉えることで解説していく予定です。
著者プロフィール
ソフトウェアテストアーキテクト。ソフトウェアテストを専門にし、テスト戦略やテスト設計に関するモデリングの手法やアジャイルなソフトウェアテストに関する研究と実践を行っています。GroovyやF#などのプログラミング言語を好んでいて、断然IntelliJ IDEA派です。Nagoya.Testing、SCMBootCamp、基礎勉強会などの勉強会を主催しています。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- テスト自動化の歴史と今後、良い/悪い事例〜システムテスト自動化カンファレンス2013レポート
テスト自動化を開発の“武器”にするための3つのポイントや、“自動化”の良い事例、悪い事例など、テストの現場を「進化させる」知見が多数紹介されたカンファンレンスの模様をレポートする。 - PHP開発者のためのテストのすゝめ(1):ユニットテストはなぜ必要なの?
開発の全工程の中で、あまり人気がないのがテスト工程だ。ソフトウェアの品質を証明するためのテストは、なぜ低く見られてしまうのか - Railsで目指せ、情熱エンジニア(5):なぜリファクタリングは必要なのか?
今回からいよいよコードの話を始めます。もはやRubyの文化の一部だという主張もあるリファクタリング、テストについて、その意義や概要を紹介します - Railsで目指せ、情熱エンジニア(7):Railsの人気テストフレームワーク6選!
前回は具体的なWebアプリを例にして簡単なコードレビューをしました。今回からは、テストを使ったリファクタリングについて解説していきます - Railsで目指せ、情熱エンジニア(8):実例で学ぶRailsアプリのテスト方法
前回はRailsで使われるテストフレームワークをご紹介しました。今回は具体的なWebアプリを例に、簡単なテストを使ったリファクタリングについて解説します - 第1回Androidテスト祭りレポート:Android開発で泣かないための「テスト」の重要性
その自由度の高さや多様性ゆえに、さまざまな課題を抱える、Androidアプリ開発の“テスト”に焦点を当てたイベントの模様を紹介します - UX Clip(28):JavaScriptのテストを開発工数に入れてもらうには?
Webアプリの大規模化とフロントエンド領域の拡大により、JavaScriptのテストの必要性が高まっている。数ある高機能なテストフレームワークをどう使いこなせば、高速かつ高品質な開発が可能になるのだろうか。 - テスト駆動開発はプログラマのストレスを軽減するか?
テスト・ファーストとリファクタリングの技法を組み合わせてプログラム開発を行う「テスト駆動開発」。簡単な例題を試しながら、この新しい開発手法を評価、検証する - 特集:Visual Studio 2008単体テスト機能徹底活用(後編):VS 2008単体テスト機能でテスト駆動開発/NUnitからの移行
VS 2008 Pro版に搭載されている単体テスト機能を使ってテスト・ファーストを実践。NUnitからのテスト移行についても考察。 - IT用語解説系マンガ:食べ超(3):テスト駆動開発で、みずから築いた壁にドーン
テストを書け! 話はそれからだ!