Testing HTTP interactions in Rails using VCR
If your Ruby on Rails application performs any HTTP requests to an external service, you will know they are a pain to test. The issue with these requests is that they are slow. Not only that, they are unreliable. Any self respecting programmer would do their best to avoid having live HTTP requests in their tests.
I previously have found myself creating some sort of XML file to represent the expected response from the request then stubbing out any real calls to just return this pre-made response. The concept is pretty simple, but is slow to set up, especially if you have a few different scenarios to test!
Introducing VCR, https://www.relishapp.com/myronmarston/vcr
VCR uses a similar concept, however it does all the hard work, leaving time to actually do testing, instead of wrangling XML or JSON fixtures. VCR is a ruby gem written by Myron Marston that records HTTP responses and saves them to files called ‘cassettes’. The cassettes are recorded the first time tests are run and saved to a corresponding file, similar to a fixture. These cassettes can be committed to source control, so other users wont even know they are there. Tests will just run as per usual. Great stuff.
Without further ado, here are the steps to get VCR working in an existing project.
Add VCR and fakeweb to the test environment in your Gemfile. Multiple HTTP stubbing libraries are supported; we are using fakeweb.
# Gemfile group :test do gem "vcr", "~> 2.0.0.rc1" gem "fakeweb" end
Run a 'bundle install'.
Next up we configure VCR in test/test_helper.rb
VCR.configure do |c| c.cassette_library_dir = 'test/vcr_cassettes' c.hook_into :fakeweb end
When testing, the aim is to use VCR anywhere where there is a HTTP request. The first time the tests are run, VCR will store the response of the request in a file called a cassette. Every successive request will then use the saved response to simulate the actual response of the external tracking service.
Here is an example of VCR in use to test the TrackingApi model. Note that this test is using Shoulda, but VCR can be used in conjunction with the testing framework of your preference.
context "with a delivered package" do
setup do
@tracking_code = 'AB123456789YZ'
VCR.use_cassette('delivered_parcel') do
@msg, @code = TrackingApi.query(@tracking_code)
end
end
should "have the correct code" do
assert_equal TrackingApi::RETURN_CODE_FINISHED, @code
end
end
In the above example, the TrackingApi.query method does a HTTP GET to an external API to check on the status of a parcel. The response is saved as 'test/vcr_cassettes/delivered_parcel.yml'.
It's that easy to get going and is configurable enough to be fit most situations.
There are two main benefits to using VCR to stub external requests:
- It's faster to setup and more realistic than manually stubbing out the requests manually.
- It is maintainable. If the response changes and the application breaks, just delete the saved cassettes. VCR will record new ones, and you can fix the issues until your tests pass again.
So, I've found VCR’s cassettes more useful than I ever found normal cassettes. It's a great gem that makes testing HTTP requests painless.