在sinatra和ruby中测试机架超时

时间:2013-02-13 10:57:34

标签: ruby sinatra rack mocha rack-test

这是我认为很简单但我在测试机架超时宝石时遇到的问题。我有一个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的更多信息是here。我正在尝试设置一个测试,我可以发送一个请求,我知道这个请求会花费几秒钟才会失败。

这是迄今为止的测试

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. 在{sleep 3}
  2. 的测试中覆盖'/ dosomething'方法
  3. 执行与上述相同但使用存根或模拟库
  4. 而不是在测试中使用get“/ dosomething”,创建一个net :: http响应,它将使请求保持打开状态。
  5. 对此的任何想法都将非常感激。

1 个答案:

答案 0 :(得分: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