2

这是我认为很简单的事情,但我在测试 rack-timeout gem 时遇到了问题。我有一个带有端点的 sinatra 基类,它执行一些逻辑。

module MyModule
  class MySinatra < Sinatra::Base
    use Rack::Timeout
    Rack::Timeout.timeout = 10

    get '/dosomething' do
       #do the normal logic.
    end
  end
end

有关 rack-timeout gem 的更多信息在这里。我正在尝试设置一个测试,我可以发送一个我知道需要几秒钟才能失败的请求。

这是到目前为止的测试

require "test/unit"
require "mocha/setup"
require 'rack/timeout'

def test_rack_timeout_should_throw_timed_out_exception_test
  Rack::Timeout.stubs(:timeout).returns(0.0001)
  assert_raises TimeoutError do
      get "/dosomething"
  end
  Rack::Timeout.unstub
end

有很多方法可以做到这一点,但我不确定它们将如何实施

  1. 将“/dosomething”方法作为测试的一部分覆盖到 {sleep 3}
  2. 执行与上述相同的操作,但使用 stubbing 或 mocking 库
  3. 不要在测试中使用 get "/dosomething",而是创建一个 net::http 响应来保持请求打开。

对此的任何想法将不胜感激。

4

1 回答 1

1

首先,您的测试实际上不会通过,因为错误没有传递给测试。它只在服务器端提出。幸运的是, rack-test 提供了last_response.errors检查是否有错误的方法。因此,我将按如下方式编写上述测试:

def test_rack_timeout_should_throw_timed_out_exception
  Rack::Timeout.stubs(:timeout).returns(0.0001)

  get '/dosomething'

  assert last_response.server_error?, 'There was no server error'
  assert last_response.errors.include?('Timeout::Error'), 'No Timeout::Error raised'

  Rack::Timeout.unstub
end

现在唯一要做的就是通过覆盖路由来模拟缓慢的响应。起初看起来很简单,但当我拿到它时,我意识到这根本不是那么简单。我摆弄了很多,并在这里想出了这个:

class Sinatra::Base
  def self.with_fake_route method, route, body
    old_routes = routes.dup
    routes.clear
    self.send(method.to_sym, route.to_s, &body.to_proc)
    yield
    routes.merge! old_routes
  end
end

它将允许您在传递给该方法的块内临时仅使用一条路线。例如,现在您可以通过以下方式模拟慢速响应:

MyModule::MySinatra.with_fake_route(:get, '/dosomething', ->{ sleep 0.0002 }) do
  get '/dosomething'
end

请注意,get '/dosomething'块内部不是临时路由的定义,而是机架测试触发模拟请求的方法。实际的覆盖路由以 的参数形式指定with_route

这是我能想到的最好的解决方案,但我希望看到一种更优雅的方式来解决这个问题。

完整的工作示例(在 Ruby 1.9.3.p385 上运行):

require 'sinatra/base'
require 'rack/timeout'

module MyModule
  class MySinatra < Sinatra::Base
    use Rack::Timeout
    Rack::Timeout.timeout = 10

    get '/dosomething' do
      'foo'
    end
  end
end




require 'test/unit'
require 'rack/test'
require 'mocha/setup'

class Sinatra::Base
  def self.with_fake_route method, route, body
    old_routes = routes.dup
    routes.clear
    self.send(method.to_sym, route, &body)
    yield
    routes.merge! old_routes
  end
end

class Tests < Test::Unit::TestCase
  include Rack::Test::Methods

  def app
    MyModule::MySinatra
  end

  def test_rack_timeout_should_throw_timed_out_exception
    Rack::Timeout.stubs(:timeout).returns(0.0001)

    MyModule::MySinatra.with_fake_route(:get, '/dosomething', ->{ sleep 0.0002 }) do
      get '/dosomething'
    end

    assert last_response.server_error?, 'There was no server error'
    assert last_response.errors.include?('Timeout::Error'), 'No Timeout::Error raised'

    Rack::Timeout.unstub
  end
end

产生:

1 tests, 2 assertions, 0 failures, 0 errors, 0 skips
于 2013-02-19T22:40:48.307 回答