How to test code that works with an external API: a stub on Sinatra
Vasily Polovnyov
Posted on May 14, 2022
Imagine a situation: your application relies on an external service responsible for card charges. The service has a tiny API: get the card number (mask) by user ID, charge X
dollars from the user. How do we test the code that works with this API?
Of course, you can actually request that external service in the tests. Maybe it even has a sandbox for such cases. But this solution is not so efficient. First of all, it slows down the tests: every request has a time cost. Secondly, it makes tests flaky: network issues will cause them to randomly fail. Thirdly, it's not always possible: the external service may have no sandbox or have strict request limits.
In such cases, it is better to use stubs for external services. I use four options of stubs, depending on the external service: is it my own or someone else's, stable or frequently changing?
One of the options is a fake service on Sinatra. Such as this one:
let(:fake_api) do
Class.new Sinatra::Base do
get "/users/:user_id/card" do
content_type :json
{ number: "4111...1111" }.to_json
end
end
end
before do
stub_request(:any, /api.nanocashier.com/).to_rack(fake_api)
end
There are two interesting things in the above code. Firstly, Class.new
with the Sinatra::Base
parent in let
, so you don't add a global constant from the test. Secondly, the stub_request
of Webmock, which routes all requests to the rack application.
I use such rack-based stubs when I have to mock an external service which doesn't have its own stubs (e.g. stripe-ruby-mock) and making adapter-like stub is not possible.
Posted on May 14, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.