Инъекционные зависимости в приложении Sinatra
Я пишу приложение Sinatra, которое вызывает некоторые внешние службы. Я хочу, очевидно, мои тесты, чтобы избежать вызова реальных сервисов, так что предположим, что у меня есть этот
class MyApp < Sinatra::Base
get '/my_method' do
@result = ExternalServiceHandler.new.do_request
haml :my_view
end
end
И в моем тесте
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
Я могу думать о двух способах введения этого. Я могу вызвать метод экземпляра или передать зависимость через конструктор. Так или иначе, поскольку стойка, похоже, не дает мне доступ к текущему экземпляру приложения, я нахожу это невозможным.
Я могу объявить метод класса для этого, но я предпочел бы работать с экземплярами, если это возможно. Чтобы потенциально возможно иметь разные инъекции в каждом случае и избегать глобального состояния, которое может повредить другие тесты, если я забуду состояние отката.
Есть ли способ сделать это?
Спасибо заранее.
Ответы
Ответ 1
Мне удалось сделать это с помощью
describe "my app" do
def app
@INSTANCE
end
before do
@INSTANCE ||= MyApp.new!
end
#tests here
end
Хотя мне особенно не нравится использование нового! перегрузка на момент его работы. Я могу использовать экземпляр, который будет использоваться с каждым тестом с помощью app.whatever_method
Ответ 2
Кажется, есть несколько вариантов. Вы можете либо передавать зависимости через конструктор, либо использовать настройки.
Конструктор Арг
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
И в спецификации:
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
Настройки
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
И в спецификации:
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