RSpec little hacks

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 にしますか? それはさすがに。で、私はこうします。以下で、 HugaActiveRecord::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のほうが適切なんだっけか。英語に詳しい人、教えてください。