让Rails测试意识到Rails内部链外的Rack中间件

时间:2011-02-16 12:57:38

标签: ruby-on-rails testing integration integration-testing rack

上下文:应用程序使用一个Rack中间件,必须在config.ru中设置,而不是Rails的内部中间件链。这是出于与此问题无关的原因。

问题:如何让我的测试(功能和集成)了解这个中间件?

我将举例说明。让我们创建一个原始的Rails 3应用程序,使用rack-rewrite进行说明。

# /config/initializers/example.rb
Rails.application.middleware.insert 0, 'Rack::Rewrite' do
 r301 '/so', 'http://stackoverflow.com'
end

# /test/integration/the_test.rb
require 'test_helper'

class TheTest < ActionDispatch::IntegrationTest
 test "redirect from /so to http://stackoverflow.com" do
   get '/so'
   assert_redirected_to 'http://stackoverflow.com'
 end
end

如果您运行上述测试,一切都很好,使用浏览器,您可以确认访问路径/so确实会将您重定向到StackOverflow。

很酷,让我们现在在Rails之外设置它。删除上述文件/config/initializers/example.rb,然后将config.ru更改为以下内容:

# /config.ru
require ::File.expand_path('../config/environment',  __FILE__)

map '/so' do
  run Rack::Rewrite do
    r301 '', 'http://stackoverflow.com'
  end
end

map '/' do
  run Deleteme::Application
end

现在,测试将停止工作。如果您使用浏览器访问/so,则可以使用该功能。只是测试不知道Rack设置。

2 个答案:

答案 0 :(得分:16)

(感谢Dan Croak让我走上正轨。这个答案完成了他所说的话。)

简短回答

无法进行功能测试。

对于集成测试,请将以下内容添加到test_helper.rb:

ActionDispatch::IntegrationTest.app = Rack::Builder.new do
  eval File.read(Rails.root.join('config.ru'))
end

长答案

功能测试只是针对控制器运行的单元测试。当你说get :index时,你没有解决路线问题。相反,这是调用index方法的简写,其中包含对HTTP方法提及的请求环境为GET。

因此,您的Rack堆栈不会影响功能测试。

集成测试是另一回事。您正在那里测试您的完整堆栈,因此您也需要测试您的Rack中间件。唯一的问题是,默认情况下,这些测试只能看到使用Rails自己的API添加的中间件。

但我说“默认情况下”,因为Rails提供了一种方法来改变Rack堆栈的测试。默认情况下,集成测试会针对其请求调用Rails.application。您可以将其更改为适合您需求的任何内容。

通过使用上面的代码,我们使用Rack::Builder创建一个ad-hoc Rack应用程序。在块中,您可以放置​​通常放在config.ru文件中的任何代码。为避免代码重复,eval加载了config.ru文件。现在,我们的集成测试将加载与应用服务器公开的应用程序完全相同的应用程序。

答案 1 :(得分:4)

我认为问题在于ActionDispatch::IntegrationTest使用您的Rails.application作为其应用的Rack应用。

因此,如果您可以将Rack::Rewrite规则包装到某些Rack中间件中,您应该能够覆盖您在测试中测试的Rack应用程序:

ActionDispatch::IntegrationTest.app = MyRewriteMiddleware