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
上は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(@array).should be(:hoge)
普通ならこう書くところです。
@array.each do |item| item.should be(:hoge) end
この量化指定子`all_of'を実装しました。CodeReposに上げてあります。
http://coderepos.org/share/browser/lang/ruby/misc/spec_qualifiers.rb
そういえば、これ、英語としてはall_ofよりeach_ofのほうが適切なんだっけか。英語に詳しい人、教えてください。