Reputation: 57
Im writing a test for this service.
def run
sort_offers(product).each do |product_code|
......
offer.update(poduct_params)
Importer::Partner.get_details(product_code).new
end
end
It's calling a service which in some cases will override the values that were saved when running offer.update(product_prams)
. How would I go about skipping the service call within my test?
Here is the example of my test
context 'is valid' do
.... .....
before do
Importer::ProductCodes(product).run
end
it ......
end
Upvotes: 1
Views: 3126
Reputation: 106932
I would stub Importer::Partner.get_details
to return a double
that responds to new
:
context 'is valid' do
before do
allow(Importer::Partner).to receive(:get_details).and_return(double(new: nil))
end
# it ...
end
Depending on your needs you might want to add an expectation that the mock was called with the correct parameters and that new
was actually called on the mock too:
context 'is valid' do
let(:mock) { double(new: nil) }
before do
allow(Importer::Partner).to receive(:get_details).and_return(double(new: nil))
end
it "calls the service" do
an_instance.run
expect(Importer::Partner).to have_received(:get_details).with(
foo: 'bar' # the arguments you would expect
)
expect(mock).to have_received(:new)
end
end
Upvotes: 1
Reputation: 102164
RSpec has a very capable stubbing and mocking library built in (rspec mocks).
require 'spec_helper'
module Importer
class Partner
def self.get_details(product_code)
"original return value"
end
end
end
class FooService
def self.run
Importer::Partner.get_details('bar')
end
end
RSpec.describe FooService do
let(:partner_double) { class_double("Importer::Partner") }
before do
stub_const("Importer::Partner", partner_double)
allow(partner_double).to receive(:get_details).and_return 'our mocked value'
end
it "creates a double for the dependency" do
expect(FooService.run).to eq 'our mocked value'
end
end
class_double
creates a double for the class and you can set the return values by using .expect
and .allow
and the mocking interface. This is quite useful since you can stub out the new
or intialize
methods to return a double or spy.
stub_constant
will reset the constant to its previous value when the spec is done.
That said you can avoid the use of stub_constant
by using constructor injection in your services:
class PhotoImportService
attr_accessor :client, :username
def initialize(username, api_client: nil)
@username = username
@client = api_client || APIClient.new(ENV.fetch('API_KEY'))
end
def run
client.get_photos(username)
end
end
Upvotes: 0