让 Rails 测试了解 Rails 内部链之外的 Rack 中间件

发布于 2024-10-17 13:39:34 字数 1217 浏览 4 评论 0原文

上下文:应用程序使用必须在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

站稳脚跟 2024-10-24 13:39:34

(感谢 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 创建一个临时 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:

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

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 the index 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 a config.ru file. To avoid code duplication, eval loads our config.ru file. Now our integration tests will load exactly the same application that our app servers expose.

决绝 2024-10-24 13:39:34

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

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

ActionDispatch::IntegrationTest.app = MyRewriteMiddleware

I think the problem is that ActionDispatch::IntegrationTest uses your Rails.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:

ActionDispatch::IntegrationTest.app = MyRewriteMiddleware
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文