Search code examples
ruby-on-railsrspecmailchimprspec-mocks

How to stub a third party object using rspec-mock?


I'm kinda new to rspec-mock, now I'm integrating hominid to communicate with Mailchimp, I want to stub some method calls, so my request doesn't actually go to Mailchimp. Here is what I'm doing

config/initilizers/mailchimp.rb

MAILCHIMP = Hominid::API.new(ENV['MAILCHIMP_API_KEY'])
LIST_ID   = ENV['MAILCHIMP_LIST_ID']

user.rb

class User < ActiveRecord::Base
  before_create :add_subscription
  private
    def add_subscription
      MAILCHIMP.list_subscribe(LIST_ID, .......) # Some params following
    end
end

So as you see, for every user created, a list_subscribe will be call on MAILCHIMP, now I don't want list_subscribe was actually called in every test because User was used everywhere, so I config globally a stub like this:

spec/spec_helper.rb

RSpec.configure do |config|
  config.before(:each) do
    allow_any_instance_of(Hominid::API).to receive(:list_subscribe).and_return(true)
  end
end

I expected MAILCHIMP instance doesn't call list_subscribe but just return true, because if list_subscribe was called, some unexpected requests will go to Mailchimp, I want to prevent this. But my above code doesn't work, what I'm wrong? How to fix this?

Moreover, an answer is likely similar to my case here How should I stub a method globally using RSpec?

I appreciate any help & suggestion? Thanks!


Solution

  • The code you've included within config.before(:each) do will execute before each test - no sooner. However, your initializer's code will execute once, before any of the test cases are executed.

    This means that instance of Hominid::API stored in MAILCHIMP wasn't affected by the instruction to stub Hominid::API.new - it's still fully functional.

    You should place the stub on MAILCHIMP as well:

    config.before(:each) do
      allow_any_instance_of(Hominid::API).to receive(:list_subscribe).and_return(true)
      allow(MAILCHIMP).to receive(:list_subscribe).and_return(true)
    end