问题 在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

我可以想到两种注入方法。我可以调用实例方法或通过构造函数传递依赖项。无论如何,因为机架似乎没有让我访问当前的应用程序实例,我发现这是不可能的。

我可以为此声明一个类方法,但如果可能的话,我更愿意使用实例。为了保持可能在每种情况下进行不同的注射,并避免在忘记回滚状态时可能损害其他测试的全局状态。

有没有办法实现这个目标?

提前致谢。


7730
2017-09-07 12:38


起源

我喜欢你的第一个想法(直接实例方法调用)并有类似的问题。我尝试在测试中正常意义上初始化我的应用,例如 app = MyApp.new 但我无法在此实例上调用实例方法。 Sinatra :: Base中的某些东西会阻止公共实例方法吗?编辑 - 如果你喜欢这种方法,这个主题可以帮助你: stackoverflow.com/questions/12072865/... - Chris Bosco


答案:


似乎有几种选择。您可以通过构造函数传递依赖项,也可以使用设置。

构造函数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

在规范中:

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

10
2017-12-22 04:12





我终于设法做到了这一点

describe "my app" do

  def app
    @INSTANCE
  end

  before do
    @INSTANCE ||= MyApp.new!
  end

  #tests here

end

虽然我不是特别喜欢使用新的!在它正在工作的那一刻超载。我可以使用app.whatever_method与每个测试一起使用的实例


1
2017-09-10 10:15