ruby-on-railsrubyrspecrspec2ruby-mocha

Testing two different expectations with mocking


I've recently just added Devise to my first Rails3 app, and I'm having a bit of trouble with the controller tests.

I'm testing the User controller class, which is the same model that Devise uses. So at the beginning of my spec I have this:

before(:each) do
  sign_in @user = Factory.create(:user)
end

Now I can get the test passing without using mocking or stubbing like so:

describe "GET edit" do
  it "assigns the requested user as @user" do
    user = Factory(:user)
    get :edit, :id => user.id
    assigns(:user).should eql(user)
  end
end

But for educational purposes I would like to know how to get it to work with mocking and stubbing, normally it would be completely straight forward, but it seems Devise is calling User.find before the controller action, and making the test fail.

describe "GET edit" do
  it "assigns the requested user as @user" do
    user = Factory(:user)
    User.expects(:find).with(:first, :conditions => {:id => 37}).returns(user)
    get :edit, :id => '37'
    assigns(:user).should be(user)
  end
end

Also by adding twice onto the expectation this would also fail because the first call to find is different to the one I'm setting the expectation for.

Any insight would be appreciated.


Solution

  • You can specify multiple return values with either stubs or expects like so:

    require 'test/unit'
    require 'mocha'
    
    class Foo
    end
    
    class FooTest < Test::Unit::TestCase
    
      # This passes!
      def test_multiple_returns_with_stubs
        Foo.stubs(:find).returns('a').returns('b').returns('c')
    
        assert_equal('a', Foo.find('z'))
        assert_equal('b', Foo.find('y'))
        assert_equal('c', Foo.find('x'))
      end
    
      # This passes too!
      def test_multiple_returns_with_expects
        Foo.expects(:find).times(1..3).returns('a').returns('b').returns('c')
    
        assert_equal('a', Foo.find('z'))
        assert_equal('b', Foo.find('y'))
        assert_equal('c', Foo.find('x'))
      end
    end
    

    The difference, apparently, is that expects needs to know how many times it's going to be called. If you don't specify anything, it assumes once and will complain on the subsequent calls. stubs doesn't care.