RSpecをオレオレ流に使うためのイディオムが結構溜まってきてます。
RubyKaigi2007でLTに応募しようと思ったけど間に合いそうにないのでself rejectして、RejectKaigiで宣伝だけしたものの一部です。
引数バリエーション
メソッドの引数だけ微妙に変化を付けながら同じ流れを繰り返したい場合があります。たとえば、ActiveRecordモデルのvalidationのspecを書くとき。
こういうときは、本家に書いてあるやり方では it
節の中に処理を全部書くことになるのですが、しかしモデルを構築する流れ自体が数行になることもありますし、それを何度も繰り返すのは冗長です。メンテナンス性にも欠けます。
そもそも、冗長さを嫌って私は実現コードの呼び出しは全部 before
節に書きたい派なのです。
describe Hoge, "がfooしたとき" do before do @hoge = Hoge..new @hoge.foo end it "は、hanyanであること" do @hoge.should be(:hanyan) end end
こんな感じで it
節をこの後に重ねていって、個々の it
は簡素であるべきだと思います。では、引数にバリエーションを付けたい場合は? 全部を別の describe
にしますか? それはさすがに。で、私はこうします。以下で、 Huga
は ActiveRecord::Base
のサブクラスです。
describe Huga, "が、新規作成される場合" do before do @name = "ダミーの名前" @addr = "ダミーのアドレス" @creation = proc do @huga = Huga.new :name => @name, :addr => @addr @huga.save! end end it "は、名前とアドレスにより作成できること" do @creatioin.should_not raise_error end it "は、名前なしでは保存できないこと" do @name = nil @createion.should raise_error(ActiveRecord::RecordInvalid) end it "は、アドレスなしでは保存できないこと" do @addr = nil @createion.should raise_error(ActiveRecord::RecordInvalid) end end
引数バリエーション2
上はProcオブジェクトを作成しないと例外に関するspecが書けないというRSpecの一番ださいところを逆用して、ActiveRecordモデルの生成に関する引数バリエーションを付けました。では、例外の有無ではなく、戻り値に関してspecを書きたい場合は?
class Hugu def bar(param1, param2) # do something end end
上のようなクラスのspecを書きたいとき
describe Hugu, "をbarをする場合" do before do @obj1 = "dummy" @obj2 = "dummy" end before do param1 = proc{ @obj1 } param2 = proc{ @obj2 } @hugu = Hugu.new @hugu.metaclass.class_eval do define_method(:bar) do super(param1.call, param2.call) end end end it "はtrueであること" do @hugu.bar.should == true end it "は、第1引数がnilだとfalseを返すこと" do @obj1 = nil @hugu.bar.should == false end it "は、第2引数がnilだとfalseを返すこと" do @obj2 = nil @hugu.bar.should == false end end
これは結構複雑なので、うまくライブラリにまとめたいのですが、一般化するためのインターフェースを思いつきません。
all_of
前に 諸橋さん が次のように書きたいよね、と言ってました。
all_of(@array).should be(:hoge)
普通ならこう書くところです。
@array.each do |item| item.should be(:hoge) end
この量化指定子`all_of'を実装しました。 CodeRepos に上げてあります。
そういえば、これ、英語としてはall_ofよりeach_ofのほうが適切なんだっけか。英語に詳しい人、教えてください。