rspec-rails: Dynamic method verification fails when method stubbed using allow_any_instance_of on a class not previously instantiated.
Dynamic method verification fails when method stubbed using allow_any_instance_of on a class not previously instantiated.
Given a test stubs a method on a class using allow_any_instance_of, and verify_partial_doubles is enabled, rspec-rails will fail to resolve the dynamic method if it is stubbed via any_instance prior to any instance(s) of that class being instantiated.
Environment
- ruby 2.1.5
- rails (3.2.19)
- rspec-core (3.2.3)
- rspec-expectations (3.2.1)
- rspec-mocks (3.2.1)
- rspec-rails (3.2.1)
- rspec-support (3.2.2)
Steps To Reproduce:
- Create a rails app
- Generate a model with some fields:
rails g model Article title:string text:text - Add the following test:
require 'rails_helper'
RSpec.describe 'dynamic methods' do
it 'does not resolve dynamic instance methods on first call to any_instance' do
allow_any_instance_of(Article).to receive(:title).and_return 'foo'
a = Article.new
expect(a.title).to eql 'foo'
end
end
- Migrate and run the test. It will fail with the message:
RSpec::Mocks::MockExpectationError: Article does not implement #title
./spec/models/any_instance_spec.rb:5:in `block (2 levels) in <top (required)>'
-e:1:in `load'
-e:1:in `<main>'
- Change
Articlein the stub toArticle.new.class:
it 'does not resolve dynamic instance methods on first call to any_instance' do
allow_any_instance_of(Article.new.class).to receive(:title).and_return 'foo'
a = Article.new
expect(a.title).to eql 'foo'
end
- Run the test again. It will pass.
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 7
- Comments: 27 (16 by maintainers)
Model.define_attribute_methodswould force rails to load methods without doing any db queriesAs a data point: this bit me today during a project to enable partial double verification on an existing Rails codebase. Naturally a future project will be to eliminate uses of
allow_any_instance_of, but I want to finish the current job first.For now I’m going to edit the failing example to add
Article.define_attribute_methodsbefore the use ofallow_any_instance_of(Article)and move on.I believe this has to do with how activerecord works – the dynamic column methods are not initially defined, so that
Article.method_defined?(:title)lies and returns false. Our docs mention this gotcha:https://relishapp.com/rspec/rspec-mocks/v/3-2/docs/verifying-doubles/dynamic-classes
However, @JonRowe added an improvement in #1238 that addresses this for
instance_double. @JonRowe, do you think you can leverage that solution for this case, too?@00dav00 AFAIK ActiveResource is no longer part of Rails and is an external gem, as such no configuration for it belongs in rspec-rails, you could of course release a gem for adding this, the config would be roughly:
To be included in a railtie or other configuration.
Of course that assumes that Active Resource knows about its methods like Active Record does… The implementation for ActiveRecord works because it’s just lazy, the hook triggers a schema load (I think) which defines all the attributes you’d expect.
Closed by rspec/rspec-mocks#1309
This problem also hit me today.
I noticed that the test failed only if executed in isolation. So i decided to force the “loading” of the class by myself.
If you do encounter this kinda of problem with an ActiveRecord model, you should be able to do the following:
My test is now passing. If I’m wrong in any point, please let me know. 😃