Managing HTTP Requests in Rails Rspec Test
In this article, I want to show a way to manage HTTP requests test to avoid clutter in each spec file. Testing HTTP requests on a Rails application with a lot of request to internal services or external services is very important because we want to show correctness of the requests especially in micro services architecture.
How To Test HTTP Requests
Usually people are using rspec
+ webmock
in order to test requests to other services.
First thing you need to do is add webmock
to yourGemfile
group: test do
gem 'webmock'
end
After that add webmock
to rspec
helper ( spec/spec_helper
):
require 'webmock/rspec'
Optionally you can add this line to force disable all HTTP request to other services. Even this is optional, it’s highly recommended to do so, since making a real request to another service can cause unintended consequences like changing a state of the object or destroying important account.
WebMock.disable_net_connect!
Now you can add stubbing to all test that request to other service:
stub_request(:post, /some-internal-api.finc.com/).
with(body: "abc", headers: {"Content-Type" => 'application/json'}).
to_return(body: "abc")
Unclutter The Stub
So far so good. Unfortunately adding stub_request
to dozens of test files can be tedious and it’s not a good for a programmer to repeat things. We need a better way to manage all the stubbing to other services.
We can do this by using rack
middleware such assinatra
Let's add a module for stubbing request if the type of test requires stubbing. Put this in spec/spec_helper.rb
config.include Stubber, type: :request
Add the module inside the spec/support/stubber.rb
file:
module Stubber
def internal_api_stubber(method, host)
stub_request(method, host).to_rack(InternalApiWebmock)
end
end
Add the middleware in spec/support/internal_api_webmock.rb
require 'sinatra/base'
require_relative 'json_reader'class InternalApiWebmock < Sinatra::Base
include JsonReader
get '/users/*' do
if params['users']
json_response 200, 'internal-api-webmock/valid-users.json'
else
json_response 404, 'internal-api-webmock/invalid-users.json '
end
end
end
Add the json_reader
module to read a response from a prepared file.
module JsonReader
def json_response(response_code, file_name)
content_type: json
status response_code
File.open(Rails.root.to_s + '/spec/fixtures/mock_responses/' + file_name, 'rb').read
end
end
Now every time you need to make a request to that endpoint, you just need to add internal_api_stubber
to your test case or to before
block.
Conclusion
By organizing the stub_request
into it's own class we can have all these benefits:
- Manage all request to internal API to it's own class.
- Setup response anyway we want. In the example I give an example of
json
response. - We can use
internal_api_stubber
to allbefore
block that do the request.