Sending real HTTP requests from your app can have some serious drawbacks:
- Flapping tests due to connectivity issues.
- Dramatically slower test suites.
- Hitting API rate limits on 3rd party sites
- Service may not exist yet (only documentation for it).
- Non-deterministic responses can cause flapping tests.
- API may not provide sandboxes or test mode.
You also need to weigh having clear boundaries of each application vs test acuity. Properly respecting the app boundaries means that you only test that your app communicates with the collaborator(s) as specified by the API(s) and stub out the actual communication.
The con is of course that stubbing always has a cost when it comes to test acuity. You may miss bugs where the API does not work as documented or where you're simply testing for the wrong thing.
Webmock for example lets you stub out the HTTP layer completely. You can set expectations on the external calls and mock the return values.
stub_request(:post, "api.example.com").to_return(status: 201)
expect(a_request(:post, "www.example.com").
with(:body => {"a" => ["b", "c"]},
:headers => {'Content-Type' => 'application/json'})).to have_been_made
VCR on the other hand is kind of a middle road, it lets you perform real HTTP requests which it records to YML files and then plays back the results. Subsequent runs are faster and deterministic. VCR is a lot less arduous than setting up mock responses, however you still need to deal with setting up the initial state and cleaning up any side effects on the external service from your tests.
VCR.use_cassette("synopsis") do
response = Net::HTTP.get_response(URI('http://example.com'))
expect(response.body).to match("Example domain")
end
This is an example plucked from a real world app that uses the Flickr API:
RSpec.feature 'Importing photosets from flickr' do
include JavascriptTestHelpers
let(:user) { create(:admin, flickr_uid: 'xxx') }
before { login_as user }
let(:visit_new_photosets_path) do
VCR.use_cassette('photosets_import') { visit new_photoset_path }
end
scenario 'When I create a photoset it should have the correct attributes', js: true do
visit_new_photosets_path
VCR.use_cassette('create_a_photoset') do
click_button('Create Photoset', match: :first)
wait_for_ajax
end
find('.photoset', match: :first).click
expect(page).to have_content "Showcase"
expect(page).to have_content "Like a greatest hits collection, but with snow."
expect(page).to have_selector '.photo img', count: 10
end
end