■10章からはBDDの実情をコレまでのプロジェクトの進め方について比較しながら説明がされます。
●従来:
計画 > 分析 > 設計 > コーディング > テスト(ユーザー受け入れテスト、パフォーマンステスト) > 導入
●アジャイル:
・ プロセスやツールよりも個人との対話
・包括的なドキュメントよりも動くソフトウェア
・契約交渉よりも顧客とのコラボレーション
・計画に従うことよりも変化の対応
というマニフェストどおり、短いイテレーションを繰り返しながら作り上げる手法
●BDD
従来のものよりもアジャイルの方法がよいが、その方法をとることが難しいので、それをなるべく簡単に(というか確実に)するためにあるのがBDDだと読み取りました。
■11章からは「BDDとは何か」というような内容です。
●原則
・十分といったら十分
・ステークホルダーに価値をもたらす
・すべては振る舞いから
●フィーチャー(ステークホルダーが考えるひとまとまりの機能)=[ストーリー,ストーリー,ストーリ](実装する段階でひとまとまりにできるもの)
●ストーリーとは
・タイトル
・ナラティブ ←誰が[ステークホルダー]がこうしたい[フィーチャー]ので~~[利益]
as a [ステークホルダー], I want [フィーチャー] so that [利益]
もしくは
in order to [利益], a [ステークホルダー] wants to [フィーチャー]
・受け入れ基準 ←シナリオ=[ステップ,ステップ,ステップ] ←所有者はテスター
[The one where] the account is locked
[The one where] the password is invalid
それぞれのステップが、前提・イベント・結果のいずれかにあたる。シナリオを必ずこの3つでそろえる必要はない。
シナリオはステークホルダーが行うことと同じ方法でアプリケーションとやり取りするようにする。
シナリオは必ずしも統合テストである必要はない。つまり機能の部分を切り出して行うことができるということだと理解しました。
■12章ではようやくRspecの記述方法や用語の意味を説明してくれています。
・サブジェクトコード ←RSpecを使って振る舞いを指定するコード
・エクスペクテーション ←期待される振る舞いを表現したもので、サブジェクトコードに書く。(Test::Unitでいうアサーション)
・サンプルコード ←サブジェクトコードの使用法と、特定のコンテキストにおけるエクスペクテーションの実行可能なサンプル。。意味がわかりません。とにかくサブジェクトコードのまえに書く。これを記述する意味は設計とドキュメント化のためであり、ついでにこれはテストになる、と。(Test:Unitでいうテスト)。つまり設計やドキュメント化でもあるテストといったところですかね。
・サンプルグループ ←サンプルコードのグループ(Test::Unitでいうテストケース)
・スペック ←サンプルグループを記述したファイル。
用語の説明のあと、Rspecの構文に関する基本的なメソッド・保留のやりかた・before,after,around・ヘルパーメソッドの共有とサンプルの共有・入れ子のメリットデメリットなどを説明してくれます。
■ 13章ではエクスペクテーションの説明があります。
オブジェクト.should expectation
オブジェクト.should_not expectation
●expectation
include (something)
respond_to(something)
raise_error(sometiong)
●等価評価のいろいろ
○オブジェクト.should == something
×オブジェクト.should != sometiong
○オブジェクト.should_not == something
●そのほか、有働小数点演算のときどうするか・評価文が複数行にまたがるときどうするかなどがの説明もあります。
●shouldのほかにexpect{}.to change{}をつかって状態の変化が正しくされているか評価する方法が示されます。
changeの後にはchange{}.to()、change{}.from()、change{}.by()と続きます。
●エラーを期待する例も紹介されています。
expect{do_something_risky}.to raise_error
●期待される異例な状況を表現するためのthrow/catchもここで説明されます。
lambda{do_something_risky}.should throw_symbol
?expect toと同違うんでしょう?そもそもlambdaって いう単語を知らないので調べてみたらラムダ計算というのがあるみたいですね。expectとの違いは使いどころであり、代替可能なこともわかりました。
●空のマッチャとしてarray.should be_emptyが紹介されます。
●同様にRuby述語に対応していることが示されます。これを述語マッチャというらしいです。
user.should be_in_role(“admin”) は user.in_role?(“admin”) == trueと同じ
他にも
user.should be_a_~~やbe_an~~というように使ってもいいと。
でもparser.should be_can_parse(“sometext”)というのは動作はするのだがおかしい文になってしまうので、カスタムマッチャを作成してなんとかすることもできると。これは後で説明するとのことです。
●このような工夫が他にもあることの例としてmethod_missingとマッチャを組み合わせることが紹介されます。
request_parameters.has_key?(:id).should == true
↓
request_parameters.should have_key(:id)
と表現できる。have_で始まるものをhas_と解釈してくれるらしいです。
●そのほかにもコレクションに対する評価方法について、コレクションを持っているパターンとコレクションが持っているというパターンでの書き方を紹介しています。
field.players.select{|p| p.team == home_team}.length == 9←フィールドにいるホームチームを取り出して、数が9であることを確認している。
↓
home_team.should have(9).players_on(field)←ホームチームのフィールドにいるプレーヤーが9にんであることを確認している。
この例の場合はhome_teamオブジェクトにplayers_onメソッドがあることが前提となっている。ない場合はNoMethodErrorが発生する。
collection.should have(37).items
「この場合のitemsは純粋な糖衣構文です。」意味がわかりません。。Wikipediaによると糖衣構文 というのは人間にとってわかりやすくすることを目的として作られた構文ということらしいです。これ(have)について後で説明するとあるので、その時点でわかることを期待します。
●文字列もコレクションだといいます。
“this string”.should have(11).characters
●コレクションに対するエクスペクテーションの精度を表現する方法が説明されます。
haveの亜種として
・have_exactly() = have()
・have_at_least()
・have_at_most()
が紹介されます。
●haveの仕組みがここでやっと説明されます。
have()メソッドは戻り値がRSpec::Matchers::Haveクラスのインスタンスです。
コレクションに期待される数で初期化されてます。
result.should have(3).things は result.should(Have.new(3).things) と同じであると。
私なりの理解ではhaveはHaveクラスを3で初期化します。3で初期化されたHaveクラスは「コレクションの数は3に違いない」という意気込みを持ちます。resultには(変数でもメソッドでもとにかく)thingsが必要で、それがコレクションだとHaveは考えます。そしてHaveはそのコレクションのlength(なければsize)をみて3かどうかを評価すると。
要するに3とresult.things.lengthを評価しているのと同じになりますね。「人間にとってわかりやすくする」というのは時にややこしくするという側面をもちます。文章としてみたときはわかりやすいが、構文としてみたときにはブラックボックスが増えているということです。実際にlengthとsizeの意味が違った場合にややこしいことになると本文中でも指摘されています。
●演算子を使う有用なケースについて、1~10の数字を乱数として10000回出力したうち1000回ぐらいは1がでるが、ばらつきがあるはずなので出る回数を980~1020であれば問題ないとする場合
it “10000回ためしたら1は10%ぐらい出現するはず、ばらつきがあっても±2%(980~1020回)になるはず” do
result.occurrences_of(1).should be_greater_than_or_equal_to(980)
result.occurrences_of(1).should be_less_than_or_equal_to(1020)
end
↓演算子のほうがわかりやすいかも
it “10000回ためしたら1は10%ぐらい出現するはず、ばらつきがあっても±2%(980~1020回)になるはず” do
result.occurrences_of(1).should be >= 980
result.occurrences_of(1).should be <= 1020
end
使える演算子は==,=~,be <,be <=,be >,be >=です。
●ここで自動的に生成された説明についての考察がなされます。それが表現したいものになることはほとんどないので、どのように対処するかについてふんわりとした意見が書いてあります。
● サンプルの主語を明確にするためのsubject()メソッドを使用してbeforeブロックでのインスタンス変数を定義する代わりに使用すると同時に、itなどのを直接書くことができるようになります。javascriptのwith構文のような感じに見えます。
describe Person do
subject{Person.new(:birthdate => 10.years.ago)}
specify{subject.should be_eligible_to_vote}
end
↓
describe Person do
subject {Person.new(:birthdate=> 10.years.ago)}
it {should be_eligible_to_vote}
end
●暗黙的な主語が説明されます。subject構文がない場合にdescribeの後に指定したクラスをnewしてsubjectとして使用するらしいです。当然この構文が使えるのはインスタンス化に引数がないものに限られます。
describe RSpecUser do
it{should be_happy}
end
いやー。長い章でしたが、RSpecに関する胸のつかえがとれた感じがします。
RSpecはテストと同時にドキュメントでもあるので、人間によって文章として読みやすくすることがひとつの重要な役割です。
そのため糖衣構文をふんだんにつかう必要がありました。故にブラックボックスは増え、どのように記述すればよいかについてリファレンスを追っていっても単に記憶されるだけで、仕組みとしては理解できないので、なんとなく腑に落ちない状況でした。似たようなことがRubyを勉強したときもありましたが、それよりもさらに高度に(いい意味で)ややこしくされています。
また、describeもcontentもitも動作としてはほとんど同じだという点も、この仕様を受け入れづらかった原因でした。「このようにも書けるし、このようにも書ける」といったことが多く、いたずらにメソッドを増やしているようにしか思えませんでした。そういうわけで「テストする」ということに思考の視点があるときは一体どれを書けばいいのか迷うことが多々ありました。「仕様は頭の中にある。はやくテストをしなければ」とあせっていたわけです。それよりもまず「仕様」を書きましょうと。それが「テスト」になりますよと。いろいろな書き方をマスターすることで、思考の視点を「仕様を書く」ということにシフトできれば、すらすらと書けるようになる気がしてきました。