Testing Interaction with 3rd-party APIs

cool episode! @joelq for QuoteService, what was behind the decision to have a class method .fetch that instantiates the object rather than just call QuoteService.new(item).fetch directly? I have lots of adapter/service objects in the app Iā€™m working on and weā€™ve chosen to use instance methods instead of class methods for no reason other than it looks a bit more OO to us. Iā€™m curious to hear your reasoning behind your approach here.

1 Like

@evan-007, I think Joelā€™s example was just meant to be simple to read for the purposes of learning the technique.

I often find that I end up creating a class method for ease-of-setup, but that I end up having that class method simply create an instance and call an instance method with the same name:

  class QuoteService
    def self.fetch(item)
      new(item).fetch
    end

    def initialize(item)
      @item = item
    end

    def fetch
      # do stuff
    end
  end

This approach works well if you end up having temporary state in your objects that you want to handle using OO principles.

1 Like

@geoffharcourt is correct. @evan-007 I agree with you that actually doing work at the instance level is preferable from an OO point of view. While all the logic is all happening in an instance, Iā€™ll often add a class method of the same name that just instantiates the object and calls the method on it. You can think of it as an alias of sorts. Iā€™m particularly prone to use this pattern on service objects due to the way they are used.

Thereā€™s an article on our blog that digs deeper into this idea: Meditations on a Class Method

2 Likes

thanks @geoffharcourt and @joelq! that makes sense. I suppose an added benefit of using a class method is that itā€™s a bit more concise in the tests to stub out a class method vs something like

  service = double 'quote_service', fetch: valid_resp
  allow(QuoteService).to receive(:new).with(some_args).and_return service

This video was really great, I enjoyed every minute.

1 Like

Thanks guys for this interesting episode! I wonder though what approach would you take when testing interaction with a SOAP API that requires sending many requests per use case. Do you guys have any experience with that?

@sauloperez when sending multiple requests per interaction, you can still use all of the techniques described in the episode:

Stubbing the ā€œbackendā€

When unit-testing, you can create multiple stubs on the ā€œbackendā€, one for each call that you would make to it.

Stubbing the network

  • If you use VCR, it should be able to capture multiple requests and then replay them
  • If you use WebMock, you can create multiple mocks, one for each web request

Swapping out the ā€œbackendā€ for a fake

If youā€™ve created a fake backend object, you should be fine as long as it implements the same interface as the real backend object. You can make as many calls to it as you need.

Swapping out the real service for a fake

If you are making real HTTP requests to a fake server, there are no limits on how many requests you can make within a single test. As long as the proper endpoints are defined, you should be fine.

This is a common situation when dealing with API authentication, such as OAuth, where you need to make multiple requests as part of a single interaction.

2 Likes