Factory_bot: undefined method `allow' for #<FactoryGirl::SyntaxRunner:0x007fd2c9978b20>

Created on 12 Oct 2014  路  11Comments  路  Source: thoughtbot/factory_bot

Hi there,

I'm using:

    factory_girl (4.4.0)
      activesupport (>= 3.0.0)
    factory_girl_rails (4.4.1)
    rspec (3.1.0)
      rspec-core (~> 3.1.0)
      rspec-expectations (~> 3.1.0)
      rspec-mocks (~> 3.1.0)
    rspec-core (3.1.6)
      rspec-support (~> 3.1.0)
    rspec-expectations (3.1.2)
      diff-lcs (>= 1.2.0, < 2.0)
      rspec-support (~> 3.1.0)
    rspec-mocks (3.1.3)
      rspec-support (~> 3.1.0)
    rspec-rails (3.1.0)

I have this in my factory:

    after(:save) do |camera|
      camera.stub(:notify_agent).and_return true
    end

However when I run the test, I get:

Using `stub` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` instead. Called from /Users/hackeron/Development/Xanview/timeline/spec/factories/factories.rb:18:in `block (3 levels) in <top (required)>'.

OK, so I change my factory to:

    after(:save) do |camera|
      allow(camera).to receive(:notify_agent).and_return true
    end

After this change however I get this error:

     NoMethodError:
       undefined method `allow' for #<FactoryGirl::SyntaxRunner:0x007fd2c9dd0060>

Any ideas what is going wrong?

Most helpful comment

You can add to your rails_helper.rb

FactoryGirl::SyntaxRunner.class_eval do
  include RSpec::Mocks::ExampleMethods
end

All 11 comments

Hi @hackeron, there's actually no way to stub or spy with RSpec directly in FG callbacks like you're doing. In situations like this, typically we'd stub out whatever external services beforehand, or use a fake object to encapsulate external dependencies.

If you're running into these sorts of things with unit tests, I'd actually recommend you not use Factory Girl at all, instead relying on more traditional object instantiation to test outcomes more discretely.

There isn't? - but other than showing a deprecation warning, this works just fine:

after(:save) do |camera|
  camera.stub(:notify_agent).and_return true
end

I want to use the factory as normal except I want to stub out 1 method used in a callback. This is an integration test, I still want the object to save to a database and call all the relevant callbacks, but not run just the 1 single method that hits an external service. How would I do this without causing a deprecation warning?

@hackeron because it's an integration test, I'd expect nothing to be stubbed here explicitly, at least from the instance level. After looking at the example again, camera.stub works because of how RSpec's methods are added to Object, whereas allow within the callback doesn't have the same access as RSpec (since it's the factory and not within test itself, which has different scoping/methods available).

I think the bigger question is still "Why skip one specific callback?" If it's to an external service, faking out that service would likely be a better solution.

@joshuaclayton it is an external service, but it is a connection to an external rabbitMQ server which uses some abstraction and ultimately the "amqp" gem - so this is not as simple as faking out a web service, so I just stub out the method to return true - but that shows a deprecation now.

@hackeron that makes sense - I'd still try to introduce some sort of abstraction/swappable component to change interacting with rabbit with a fake. At this point, there's nothing to address in FG specifically so I'm going to close this issue.

@hackeron If you reallllly want to just stub it you could do this:

after(:save) do |camera|
  class << camera
    def notify_agent; true; end
  end
end

@MarkMurphy ooooh, that works beautifully, thank you!

You can add to your rails_helper.rb

FactoryGirl::SyntaxRunner.class_eval do
  include RSpec::Mocks::ExampleMethods
end

@printercu :+1: that works beautifully.

(Updated StackOverflow answer with this: http://stackoverflow.com/questions/9072338/should-i-stub-the-model-in-factory-girl-or-in-the-spec-file-while-testing/31129467#31129467 )

I commonly run up against this issue when trying to stub has_many associations on a model.

My instinct is to do something like this to no avail.

    trait :with_bars do
      after(:stub) do |foo, e|
        bars = build_stubbed_list(:bar, 3)
        allow(foo).to receive(:bars).and_return(bars)
      end
    end

But I've recently got something like this working

    trait :with_bars do
      after(:stub) do |foo, e|
        bars = build_stubbed_list(:bar, 3)
        bars.each { |bar| foo.association(:bars).add_to_target(bar) }
      end
    end

Just in case anyone lands here with a similar issue.

Fro those for whom include into the factory runner doesn't work you can always do this:

        RSpec::Mocks::AllowanceTarget.new(target).to(
          RSpec::Mocks::Matchers::Receive.new(method, proc {})
            .with(...).and_return(...)
        )
Was this page helpful?
0 / 5 - 0 ratings