I am re-factoring a medium-sized web application which has many unit tests and acceptance tests written using RSpec. My main goal is to clean up code for a microservice that runs as a separate rack application in production. The current test suite uses process management to start and stop the microservice, which is causing problems with test performance and complicating how the test environment manages test fixture data.
There are hundreds of tests that rely on the correct behaviour of the microservice, which means on this re-factoring pass I will not get the opportunity to fully mock out or fake the microservice itself. But I would really like to remove all the separate process-handling and the associated costs. This is now feasible since I am adding unit tests for the microservice itself, and we have a separate smoke and integration test infrastructure which already runs with the microservice handled as a separate rack-based service.
I have created a SSCE to demonstrate the problem. It is in multiple small files, fully functional and includes tests. Apologies for the overall length, but I think it is unavoidable because my question is about how to combine these multiple components.
microservice/app.rb
require "grape"
class Microservice < Grape::API
format :json
get '/main' do
{ :message => "Hello World!" }
end
end
microservice/config.ru
require File.join(File.dirname(__FILE__), "app")
run Microservice
microservice/spec/spec_helper.rb
require_relative '../app.rb'
require 'rspec'
require 'rack/test'
def app
Microservice
end
RSpec.configure do |config|
config.include Rack::Test::Methods
end
microservice/spec/microservice_spec.rb
require_relative 'spec_helper'
describe Microservice do
describe "GET /main" do
it "responds with correct JSON" do
get "/main"
expect( last_response ).to be_ok
data = JSON.parse( last_response.body )
expect( data ).to be == { "message" => "Hello World!" }
end
end
end
webapp/app.rb
require 'sinatra'
require 'json'
require 'httparty'
# This stands in for more complex config we have in reality
$microservice_url = 'http://127.0.0.1:8090/'
get '/main' do
# Calls a microservice . . . (annoyingly the service uses the same route name)
response = HTTParty.get( $microservice_url + "main" )
data = JSON.parse( response.body )
"#{data['message']}\n"
end
webapp/spec/spec_helper.rb
require_relative '../app.rb'
require 'rspec'
require 'rack/test'
def app
Sinatra::Application
end
RSpec.configure do |config|
config.include Rack::Test::Methods
end
# Is there something I can do here to load the
# microservice without launching it in a new process, and route
# the HTTParty.get in the main app to it?
$microservice_url = 'http://127.0.0.1:8888/'
webapp/spec/webapp_spec.rb
require_relative 'spec_helper'
describe "Main web app" do
describe "GET /main" do
it "responds with correct text" do
get "/main"
expect( last_response ).to be_ok
expect( last_response.body ).to include "Hello World!"
end
end
end
I can run the microservice test easily:
rspec -f d -c microservice/spec/microservice_spec.rb
But to run the webapp test, I first need to start up the microservice where the test expects to find it:
rackup -p 8888
(Different process)
rspec -f d -c webapp/spec/webapp_spec.rb
I think I can mount the microservice inside the application, from viewing questions like How to mount a Sinatra application inside another Sinatra app? but this seems geared to joining applications in a production environment, I need them to be separate there, joined only in unit tests where the helper enables it, and so far am at a complete loss on how I tell HTTParty (or any replacememt which could do what I want here) to connect.
Here's how I might stub the single call in the example (at end of webapp/spec/spec_helper.rb) - is there a way to route this to an in-process mount of the microservice instead?
require 'webmock/rspec'
include WebMock::API
stub_request( :any, /:8888\// ).to_return( :body => '{ "message":"Hello World!"}' )
WebMock provides routing to a Rack response, so package microservice
so it's available to webapp
and then you can:
require 'microservice'
stub_request( :any, /:8888\// ).to_rack( Microservice )