让 Rails 测试了解 Rails 内部链之外的 Rack 中间件
上下文:应用程序使用必须在 config.ru 中设置的 Rack 中间件,而不是 Rails 的内部中间件链。这是出于与该问题无关的原因。
问题:如何让我的测试(功能和集成)意识到这个中间件?
我将用一个例子来详细说明。为了便于说明,让我们使用 rack-rewrite 创建一个原始的 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
现在,测试将停止工作。该功能确实有效,如果您使用浏览器访问 /so
即可证明这一点。只是测试不知道机架设置。
Context: an application uses a piece of Rack middleware that must be setup in config.ru, rather than Rails's internal Middleware chain. This is for reasons not relevant to this question.
Question: how do I make my tests (functional and integration) aware of this middleware?
I'll ellaborate with an example. Let's create a pristine Rails 3 app, using rack-rewrite for illustration purposes.
# /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
If you run the above test, all is good, and with the browser you can confirm that visiting the path /so
will redirect you to StackOverflow indeed.
Cool, let's now set this up outside Rails then. Remove the file /config/initializers/example.rb
described above, and change config.ru
to the following:
# /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
Now, the test will stop working. The functionality does work, as evidenced if you visit /so
with your browser. It's only that the tests are not aware of that Rack setup.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
(感谢 Dan Croak 让我走上了正确的道路。这个答案完成了他所说的)。
简短回答
无法进行功能测试。
对于集成测试,请将以下内容添加到 test_helper.rb:
长答案
功能测试只是针对控制器运行的单元测试。例如,当您说
get :index
时,您并没有解析路由。相反,这是调用index
方法的简写,其中请求环境包含对 HTTP 方法 GET 的提及。因此,您的 Rack 堆栈不会影响功能测试是有道理的。
集成测试是另一回事。您正在那里测试您的完整堆栈,因此您也需要测试您的 Rack 中间件。唯一的问题是,默认情况下,这些测试只能看到您使用 Rails 自己的 API 添加的中间件。
但我说“默认”是因为 Rails 提供了一种方法来更改将测试的 Rack 堆栈。默认情况下,集成测试会调用
Rails.application
来处理其请求。您可以将其更改为适合您需要的任何内容。通过使用上面的代码,我们使用 Rack::Builder 创建一个临时 Rack 应用程序。在该块内,您可以放置通常放入
config.ru
文件中的任何代码。为了避免代码重复,eval
加载我们的config.ru
文件。现在,我们的集成测试将加载与我们的应用程序服务器公开的完全相同的应用程序。(Thanks Dan Croak for setting me on the right track. This answer completes what he said).
Short answer
Can't be done for functional tests.
For integration tests, add the following to your test_helper.rb:
Long answer
Functional tests are just unit tests run against controllers. When you say, for example
get :index
, you are not resolving a route. Instead, this is a shorthand for calling theindex
method, with a request environment that includes a mention to the HTTP method being GET.Therefore, it makes sense that your Rack stack does not influence functional tests.
Integration tests are a different matter. You are testing your full stack there, so you will want to test your Rack middlewares too. Only problem is that, by default, these tests only see the middlewares that you add using Rails's own API.
But I say "by default" because Rails offers a way to change what Rack stack will be tested. By default, integration tests call
Rails.application
for their requests. You can change this to anything that suits your needs.By using the code above, we use
Rack::Builder
create an ad-hoc Rack application. Inside the block, you can put any code that you would normally put inside aconfig.ru
file. To avoid code duplication,eval
loads ourconfig.ru
file. Now our integration tests will load exactly the same application that our app servers expose.我认为问题在于
ActionDispatch::IntegrationTest
使用您的Rails.application
作为其 Rack 应用程序进行测试。因此,如果您可以将该
Rack::Rewrite
规则包装到某些 Rack 中间件中,您应该能够使用以下命令覆盖您在测试中测试的 Rack 应用程序:I think the problem is that
ActionDispatch::IntegrationTest
uses yourRails.application
as its Rack app for the test.So, if you can wrap that
Rack::Rewrite
rule into some Rack middleware, you should be able to override the Rack app you're testing in your test with: