RSpecのstub!やshould_receive等で注意すること

子のテーブルのメソッドにmockメソッドを追加するときに、以下のようなコードになります。

describe "間違っている例" do
  before do
    @user = mock_model(User)

    @user.stub!(:books)
    @user.books.stub!(:size).and_return(1)
    @user.stub!(:items)
    @user.items.stub!(:size).and_return(2)
  end
end

p @user.books.size # => 2
p @user.items.size # => 2

しかし、違う子の同じメソッドを上記のようにmockすると、どちらも最後にmockしたand_return(2)が返ってきてしまいます。

これはどういうことかというと、結論からいえば、sizeメソッドが同じNilObjectにmockされてしまっているからです。
ソースでいえば、

describe "正しい例" do
  before do
    @user = mock_model(User)

    @user.stub!(:books).and_return(mock_model(Book))
    @user.books.stub!(:size).and_return(1)
    @user.stub!(:items).and_return(mock_model(Item))
    @user.items.stub!(:size).and_return(2)
  end
end

p @user.books.size # => 1
p @user.items.size # => 2

な感じでmockするクラスオブジェクトを明確に区別してあげればうまくいきます。

明示的に.and_returnを書かない場合、NilObjectをreturnしていることがミソです。