How to Test Sinatra-Based Web Services

Harlow Ward

Sinatra is a fantastic lightweight framework for building web services. We’ll use it as the application framework for the HTTP endpoints in our Service Oriented Architecture.

The testing approach will be in-process, which means that the test suite is running in the same Ruby process as the web service. This eliminates the need to run an external HTTP web server.

Application structure

Unlike Rails, Sinatra isn’t all that opinionated on how you set up your application (it has a few sensible defaults), which can lead to a lot of open questions on how to structure the application.

Here’s the directory structure we’ll use for our example application.

app/
app/models
app/my_service.rb
client/
client/lib/my_client.rb
client/my_client.gemspec
config/
spec/

Outside-in development with a Client Gem

For internal services we’ll build a client gem directly into the project. With the client embedded in our codebase we can follow outside-in development cycles – Features start with request specs from the client side, followed by the addition of end points to the service, and then unit tests within the application.

This allows the us to dogfood our client gem and bring it in as the first step of our development process.

To achieve this we need to configure a few things.

  1. Set up the project gemfile to use a local copy of the client in test mode

    Include the local client gem in test suite.

    # Gemfile
    group :test do
      gem 'my_client', path: 'client'
      gem 'webmock'
      # ...
    end
    
  2. Use webmock to send all http requests to the service

    Instead of booting up a web server every time the test suite is run we’ll mount the Sinatra service as a rack application with webmock.

    This allows the client to talk directly to the mounted rack application without going through HTTP or a web server.

    # spec/spec_helper.rb
    RSpec.configure do |config|
      config.include WebMock::API
    
      config.before(:each) do
        MyClient.base_url = 'http://www.example.com'
        stub_request(:any, /www.example.com/).to_rack(MyService)
      end
    end
    
  3. Use the client in our request specs

    Once MyService is mounted as a rack application we can use the client gem directly in our test suite.

    # spec/requests/widget_management_spec.rb
    require 'spec_helper'
    
    describe "Widget management" do
      it "creates a Widget" do
        # set up fixture data if needed
    
        response = MyClient::Widget.create(widget_params)
    
        # assert expectations on the response
      end
    end
    

Private gem hosting

To use the client gem in other projects we can use a private gem hosting service like Gemfury. This allows us to include the client via gemfile in our other projects.

# Gemfile
source 'https://452f6E403CDph10714e41@gem.fury.io/me/'
gem 'my_client'

source 'https://rubygems.org'
# ...

Takeaways

  • Sinatra is great framework for creating lightweight web services.
  • Webmock allows us to test the client in-process against a rack application.
  • Use private gem hosting to distribute the shared client.