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

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

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

我将以一个例子说明。 让我们创建一个原始的Rails 3应用程序,使用机架重写进行说明。

# /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 

现在,测试将停止工作。 该function确实有效,如您使用浏览器访问/so所certificate的/so 。 只是测试不知道Rack设置。

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

简短的回答

无法进行function测试。

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

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

答案很长

function测试只是针对控制器运行的unit testing。 当你说,例如get :index ,你没有解析路线。 相反,这是调用index方法的简写,其中包含对HTTP方法提及的请求环境是GET。

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

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

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

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

我认为问题是ActionDispatch::IntegrationTest使用您的Rails.application作为其测试的Rack应用程序。

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

 ActionDispatch::IntegrationTest.app = MyRewriteMiddleware