tl;dr You can explicitly return whatever data you want from external API requests using Webmock and stubbing requests.
How do you test a Controller that makes an API request to another website, but you want to control what exactly is returned? For example, you want to test a Controller that queries Twitter for tweets, but you don’t want to be at the mercy of whatever tweets are being created that second because you want to test how well you can scrape certain keywords.
That’s where Webmock comes in. https://github.com/bblimke/webmock
By adding the code below to your spec_helper or rails_helper file, you can stub external API requests and control the data that is returned to your controllers.
config.before(:each) do
stub_request(:get, /some.website.api/).
with(headers: {‘Accept’: ‘*/*’, ‘User-Agent’: ‘Ruby’ }).
to_return(status: 200, body:’{“test”: 0}’, headers: {})
end
Lets go through each line:
config.before(:each)
do This tells RSpec to run the following code before each spec.
stub_request(:get, /some.website.api/).
This is the first part of the stub_request method we get from Webmock. It specifies the type of request (:get -> GET), but you can also use any other RESTful request, including the symbol :any to stub any request that comes in. The second part is the URI. You can use reqex as used above, or strings such as "www.example.com". Basically, this stub_request is now waiting for a GET request to be made to a URI that matches the reqex /some.website.api/
with(header: {'Accept': '*/*', 'User-Agent': 'Ruby' }).
In the second part, you can explicitly set the header information you want to match. If in your code you make a GET request to /some.website.api/, but the ‘User-Agent’ is something other than ‘Ruby’, then this stub_request will not be invoked.
to_return(status: 200, body: '{"test": 0}', headers: {})
Finally we reach the line that specifies what response we want when our code calls the API at /some.website.api/. In particular, we can set the status code (Here is 200 which means OK). The body, unsurprisingly, is the data returned to us. In can be one of the following types: [Proc, IO, Pathname, String, Array] . You can also explicitly set the headers of the response.
To Summarize: By using the Webmock gem and the code above in your RSpec helper file, you can stop API requests made by your code and return whatever data you would like.
More info here: https://robots.thoughtbot.com/how-to-stub-external-services-in-tests
Webmock Gem: https://github.com/bblimke/webmock
Happy coding!
Top comments (3)
A quick note, often you'll want to return status 200 with a specific body. Since status 200 is
to_return
s default, you can simply writeto_return(body: "something")
.Wow, thanks for the info! The less code you write, the less chance for bugs and errors!
Good advice. On smaller projects I tend to do something similar but on my work production application we use the VCR gem which records one call to a yml (a cassette) and then every time the test runs it uses that yml in place of the api response.