0

I have:

  • a web service wrapper that calls a 3rd party API (lib)
  • a service that calls that wrapper and does some finagling (service)

Testing the lib class with VCR works great, but I am not sure how to test the service. Specifically, how can I stub the API call inside the service?

class GetBazsForBars
  def self.call(token)
    bazs = Foo.new(token).bars

    # do some other stuff
    bazs
  end
end

And the test:

require 'rails_helper'

RSpec.describe GetBazsForBars, type: :service do
  describe ".call" do
    let(:token) { Rails.configuration.x.test_token }
    let(:bazs)  { GetBazsForBars.call(token) }

    it { expect(bazs.any?).to eq(true) }
  end
end

I'd prefer not to pass in the Foo to the service.

In Payola the author uses a StripeMock to dummy up the responses. Can I write up something similar, like a FooMock that calls VCR and have the service use that instead?

Not sure how to do this with Ruby, I am used to C# / Java DI passing to constructors.

Thanks for the help.

Update

Moved to a full answer

blu
  • 12,905
  • 20
  • 70
  • 106

2 Answers2

1
foo = double(bars: 'whatever')
allow(Foo).to receive(:new).with(:a_token).and_return foo

or

allow(Foo).to receive(:new).with(:a_token)
  .and_return double(bar: 'whatever')
Mori
  • 27,279
  • 10
  • 68
  • 73
0

@mori provided a good starting point and I ended up...

1) stubbing the const of the service name:

require 'rails_helper'

RSpec.describe GetBazsForBars, type: :service do
  describe ".call" do
    before { stub_const("Foo", FakeFoo) } # <-- added this

    let(:token) { Rails.configuration.x.test_token }
    let(:bazs)  { GetBazsForBars.call(token) }

    it { expect(bazs.any?).to eq(true) }
  end
end

2) creating a fake foo wrapper in /spec/fakes:

class FakeFoo < Foo
  def bars
    VCR.use_cassette("foo_bars") do
      super()
    end
  end
end

This means in my service I can just Foo.new and it handles test and real life the same (great integration test). And I didn't have setup any mocking chains in the test that would require an actual instance to get the VCR call correct.

There may be an easier way, but the code looks pretty good to me so ship it.

blu
  • 12,905
  • 20
  • 70
  • 106