Wonderboy111
Wonderboy111

Reputation: 57

How do you skip a service call in a rspec test

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

Answers (2)

spickermann
spickermann

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

max
max

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

Related Questions