2

これは簡単なことだと思っていましたが、rack-timeout gem のテストに関して問題が発生しています。いくつかのロジックを実行するエンドポイントを持つシナトラ基本クラスがあります。

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. {sleep 3} へのテストの一部として '/dosomething' メソッドをオーバーライドします
  2. 上記と同じことを行いますが、スタブ ライブラリまたはモッキング ライブラリを使用します。
  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 に答える