I'm writing a Sinatra app that calls some external services. I want obviously my tests to avoid calling the real services so suppose at the moment I have this
class MyApp < Sinatra::Base
get '/my_method' do
@result = ExternalServiceHandler.new.do_request
haml :my_view
end
end
And in my test
describe "my app" do
include Rack::Test::Methods
def app() MyApp end
it "should show OK if call to external service returned OK" do
@external_service_handler = MiniTest::Mock.new
@external_service_handler.expect :do_request, "OK"
#Do the injection
get '/my_method'
response.html.must_include "OK"
end
it "should show KO if call to external service returned KO" do
@external_service_handler = MiniTest::Mock.new
@external_service_handler.expect :do_request, "KO"
#Do the injection
get '/my_method'
response.html.must_include "KO"
end
end
I can think of two ways to inject this. I can call an instance method or passing the dependency through constructor. Anyway since rack does not seem to give me access to current application instance I'm finding this impossible.
I can declare a class method for this but I'd prefer working with instances if possible. To keep potentially possible to have different injections in each case and avoiding global state that could harm other tests if I forget to rollback state.
Is there any way to accomplish this?
Thanks in advance.
It seems as though there are a couple of options. You can either pass the dependencies in through the constructor, or use settings.
Constructor Args
class MyApp < Sinatra::Base
def initialize(app = nil, service = ExternalServiceHandler.new)
super(app)
@service = service
end
get "/my_method" do
@result = @service.do_request
haml :my_view
end
end
And in the spec:
describe "my app" do
include Rack::Test::Methods
let(:app) { MyApp.new(service) }
let(:service) { double(ExternalServiceHandler) }
context "when the external service returns OK" do
it "shows OK" do
expect(service).to receive(:do_request).and_return("OK")
get '/my_method'
response.html.must_include "OK"
end
end
context "when the external service returns KO" do
it "shows KO" do
expect(service).to receive(:do_request).and_return("KO")
get '/my_method'
response.html.must_include "KO"
end
end
end
Settings
class MyApp < Sinatra::Base
configure do
set :service, ::ExternalServiceHandler.new
end
get "/my_method" do
@result = settings.service.do_request
haml :my_view
end
end
And in the spec:
describe "my app" do
include Rack::Test::Methods
let(:app) { MyApp.new }
let(:service) { double(ExternalServiceHandler) }
before do
MyApp.set :service, service
end
context "when the external service returns OK" do
it "shows OK" do
expect(service).to receive(:do_request).and_return("OK")
get '/my_method'
response.html.must_include "OK"
end
end
context "when the external service returns KO" do
it "shows KO" do
expect(service).to receive(:do_request).and_return("KO")
get '/my_method'
response.html.must_include "KO"
end
end
end
I finally managed to do this with
describe "my app" do
def app
@INSTANCE
end
before do
@INSTANCE ||= MyApp.new!
end
#tests here
end
Although I don't particularly like using the new! overloading at the moment it's working. I can use the instance that will be used with each test with app.whatever_method
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With