Тестирование тайм-аута в синатре и рубине

Я подумал, что это будет просто, но у меня возникли проблемы с тестированием самоцвета в стойке. У меня есть базовый класс sinatra с конечной точкой, которая выполняет некоторую логику.

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

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

Больше информации о стойке-тайм-ауте здесь. Я пытаюсь настроить тест, в котором я могу отправить запрос, который, как я знаю, займет более нескольких секунд, чтобы он не прошел.

Вот тест пока

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, который будет держать запрос открытым.

Любые мысли по этому поводу будут очень цениться.

1 ответ

Прежде всего, ваш тест на самом деле не пройдет, потому что ошибка не передается тесту. Он поднимается только на стороне сервера. К счастью, стойка-тест обеспечивает 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
Другие вопросы по тегам