Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

undefined method `mock' for some controller specs that expect a mocked model should receive a message #218

Closed
modellurgist opened this Issue · 4 comments

2 participants

@modellurgist

To replicate:

  • use ree-1.8.7-2010.02 (via RVM with a gemset, but not sure if that affects result)
  • use rails 3.0.0, rspec-rails 2.0.0.beta.22
  • create a new rails project and cd into its directory
  • $ rails g rspec:install
  • $ rails g scaffold Project (where Project is arbitrary model name)
  • create and migrate the database
  • $ rails g scaffold Project # an arbitrary model name
  • copy the spec for ProjectController's #create method to the specs for its #new method
  • modify that spec example to call 'get :new' but otherwise leave it the same
    
    it "saves the project" do
      Project.stub(:new).with({'these' => 'params'}) { mock_project(:save => true) }
      mock_project.should_receive(:save)
      get :new
    end 
    
  • in the Project controller's #new method add a line to call save on @project (this step is not necessary to reproduce the error)
  • execute $ rake spec
  • instead of a failure (on just the #new spec we added), it gives this error:
    
    Failure/Error: @mock_project ||= mock_model(Project, stubs).as_null_object
     undefined method mock' for #<RSpec::Core::ExampleGroup::Nested_2::Nested_1::Nested_2:0x103dcc9a0>
     # /Users/myuser/.rvm/gems/ree-1.8.7-2010.02@mygemset/gems/actionpack-3.0.0/lib/action_dispatch/testing/assertions/routing.rb:177:inmethod_missing'
     # /Users/myuser/.rvm/gems/ree-1.8.7-2010.02@mygemset/gems/rspec-rails-2.0.0.beta.22/lib/rspec/rails/mocks.rb:71:in mock_model'
     # ./spec/controllers/projects_controller_spec.rb:6:inmock_project'
     # ./spec/controllers/projects_controller_spec.rb:37
    
@dchelimsky
Owner

What happens if you change the spec to this:

it "saves the project" do
    mock_project.should_receive(:save) {true}
    Project.stub(:new).with({'these' => 'params'}) { mock_project }
    get :new
  end
@dchelimsky
Owner

The problem is that the mock_project method only adds the stubs the first time it is invoked. The first line in the example uses the block format to spec the return value:

Project.stub(:new).with({'these' => 'params'}) { mock_project(:save => true) }

That block gets evaluated when Project receives new, which is triggered by the third line of the example:

get :new

This means that the first invocation of mock_project happens on the 2nd line of the example:

mock_project.should_receive(:save)

Therefore the stub of save is never set on the mock project.

I'm going to change the generated mock_project to read like this:

def mock_project(stubs={})
  @mock_project ||= mock_model(Project).as_null_object
  @mock_project.stub(stubs)
end

That will make it so the stubs are additive (i.e. each invocation of mock_project adds the new stubs. If you do that now, this should work as you expect.

@dchelimsky
Owner

Generated mock_[model] method adds (optional) stubs on each reference.

  • This fixes an edge case bug that appears when defining stubs using the lazy-eval'd block form (obj.stub(:m) {v}).
  • Closed by ae50bf9.
@dchelimsky
Owner

FYI - the method should be this (not what I posted two comments ago):

  def mock_project(stubs={})
    (@mock_project ||= mock_model(Gadget).as_null_object).tap do |project|
      project.stub(stubs) unless stubs.empty?
    end
  end
@amatsuda amatsuda referenced this issue from a commit
@dchelimsky dchelimsky Generated mock_[model] method adds (optional) stubs on each reference.
- This fixes an edge case bug that appears when defining stubs using the
  lazy-eval'd block form (obj.stub(:m) {v}).
- Closes #218.
ae50bf9
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.