简体   繁体   中英

Testing rack-timeout in sinatra and ruby

This is something that I thought would be straightforward but I'm having issues around testing the rack-timeout gem. I have a sinatra base class with an endpoint which does some logic.

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

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

More information on the rack-timeout gem is here . I'm trying to setup a test where I can send a request which I know will take more than a few seconds in order for it to fail.

Here is the test so far

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

There are a number of ways this could be done but I am not sure how they would be implemented

  1. Override the '/dosomething' method as part of the test to {sleep 3}
  2. Do the same as above but with a stubbing or mocking library
  3. instead of using get "/dosomething" in the test, create a net::http response which will keep the request open.

Any thoughts on this would be very much appreciated.

First of all your test will not actually pass, because the error is not handed through to the test. It is only raised on the server side. Luckily, rack-test provides the last_response.errors method to check whether there were errors. Therefore i would write the above test as follows:

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

Now the only thing left to do is to simulate a slow response by overriding the route. It seemed simple at first but then i realized it is not so simple at all when i got my hands on it. I fiddled around a lot and came up with this here:

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

It will allow you to temporarily use only a route, within the block you pass to the method. For example now you can simulate a slow response with:

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

Note that the get '/dosomething' inside the block is not the definition of the temporary route, but a method of rack-test firing a mock request. The actual override route is specified in form of arguments to with_route .

This is the best solution i could come up with but i would love to see a more elegant way to solve this.

Complete working example (ran on 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

produces:

1 tests, 2 assertions, 0 failures, 0 errors, 0 skips

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM