【问题标题】:Testing rack-timeout in sinatra and ruby在 sinatra 和 ruby​​ 中测试机架超时
【发布时间】:2013-02-13 10:57:33
【问题描述】:

我认为这很简单,但我在测试 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 的更多信息是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. 将“/dosomething”方法作为测试的一部分覆盖到{sleep 3}
  2. 与上述相同,但使用存根或模拟库
  3. 不要在测试中使用 get "/dosomething",而是创建一个 net::http 响应以保持请求打开。

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

【问题讨论】:

  • 存根有什么问题(仅用于超时测试)?
  • @padde 这已经在代码中完成了。超时设置为 0.0001。除非测试超时是一个非常小的数字 EG 0.000000000000000000000001(比进程在机器上可以处理的速度更快),否则测试将始终失败。此外,更快的机器将比其他机器更快地处理“/dosomething”方法。所以在某些机器上,测试会超时(这是我们想要的),而在其他较慢的机器上,测试会成功。
  • 抱歉,我的意思是用至少与超时时间一样长的睡眠调用覆盖 get '/dosomething' 有什么问题?
  • @padde 我已经将此列为潜在的解决方案(第 2 号)。但是我不确定它会如何实现。

标签: ruby sinatra rack ruby-mocha rack-test


【解决方案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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-16
    • 1970-01-01
    相关资源
    最近更新 更多