Managing HTTP Requests in Rails Rspec Test

Karuna Murti
FiNC Tech Blog
Published in
2 min readOct 29, 2017
Rspec

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+ webmockin order to test requests to other services.

First thing you need to do is add webmockto yourGemfile

group: test do 
gem 'webmock'
end

After that add webmockto rspechelper ( 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 rackmiddleware 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.rbfile:

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_readermodule 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_requestinto it's own class we can have all these benefits:

  1. Manage all request to internal API to it's own class.
  2. Setup response anyway we want. In the example I give an example of jsonresponse.
  3. We can use internal_api_stubberto all beforeblock that do the request.

--

--

Karuna Murti
FiNC Tech Blog

Senior polyglot programmer and sysadmin with management experience. Ruby, Rust, Java, Python, JS, C ++