【问题标题】:Why doesn't Minitest's assert_raises work as expected in this case?在这种情况下,为什么 Minitest 的 assert_raises 不能按预期工作?
【发布时间】:2014-04-24 08:47:14
【问题描述】:

我正在尝试使用 Action Controller bug report template 来解决 Rails 中的一个奇怪行为。

作为记录,这是模板中的控制器:

class TestController < ActionController::Base
  include Rails.application.routes.url_helpers

  def index
    render text: 'Home'
  end
end

我已经添加了一个缺少动作的路由:

routes.draw do
  get '/' => 'test#index'
  get '/missing' => 'test#missing'
end

我试图断言AbstractController::ActionNotFound 在遵循该路线时引发:

class BugTest < Minitest::Test
  include Rack::Test::Methods

  def test_missing
    assert_raises(AbstractController::ActionNotFound) { get '/missing' }
  end

  private
    def app
      Rails.application
    end
end

预期行为:绿色测试。

实际行为:

# Running tests:

D, [2014-04-24T09:17:41.948985 #4497] DEBUG -- :
D, [2014-04-24T09:17:41.949029 #4497] DEBUG -- :
I, [2014-04-24T09:17:41.949094 #4497]  INFO -- : Started GET "/missing" for 127.0.0.1 at 2014-04-24 09:17:41 +0200
F, [2014-04-24T09:17:41.951337 #4497] FATAL -- :
AbstractController::ActionNotFound (The action 'missing' could not be found for TestController):
[stacktrace]

  1) Failure:
BugTest#test_missing [action_controller_gem.rb:45]:
AbstractController::ActionNotFound expected but nothing was raised.

所以,基本上,引发了异常,但 Minitest 声称没有引发任何事情。我在这里被难住了。

为什么 Minitest 不能断言 AbstractController::ActionNotFound 被提出?我查看了 Rack::Test::Methods 以排除 get 与线程或其他东西一起使用但找不到任何东西。

我也看过implementation of assert_raises——没什么明显的。

为什么assert_raises(AbstractController::ActionNotFound) { get '/missing' } 不通过?

(别介意这已经在 Rails 中测试过了。我正在努力克服这个问题。)

【问题讨论】:

  • 请显示TestController。和完整的BugTest
  • 为清楚起见添加了它。我没有修改TestController 的模板(在第一句中链接),只是将test_missing 添加到BugTest
  • TestController 应该继承自 ApplicationController。或者你有完整的app 示例?
  • 我拿了我们现在都引用的AC模板,添加了一个路由并添加了一个测试方法。为什么要改TestController的超类?
  • 我的意思是你有完整的列表或块?因为我用这个,这个对我有用。

标签: ruby unit-testing ruby-on-rails-4 minitest


【解决方案1】:

这不是assert_raises 的问题,它继续工作得很好。您遇到的问题是您在 Rails 应用程序中提出的异常正在由您的 Rails 应用程序处理,而不是传播到您的测试。调用 get '/missing' 会引发 AbstractController::ActionNotFound 错误,但随后您的应用会处理错误并向客户端(您的测试)返回适当的响应(404 或 500)。

好的,那么您将如何测试您的控制器是否确实引发了您所期望的错误?通常你只需使用ActionController::TestCase 测试来测试你的控制器。但是,当您在ActionController::TestCase 中没有的控制器上调用操作时,您会得到不同的错误。所以如下:

require "test_helper"

class TestControllerTest < ActionController::TestCase

  def test_missing
    assert_raises AbstractController::ActionNotFound do
      get :missing
    end
  end
end

产生以下输出:

  1) Failure:
TestControllerTest#test_missing [test/controllers/test_controller_test.rb:6]:
[AbstractController::ActionNotFound] exception expected, not
Class: <ActionController::UrlGenerationError>
Message: <"No route matches {:action=>\"missing\", :controller=>\"test\"}">
---Backtrace---
test/controllers/test_controller_test.rb:7:in `block in test_missing'
test/controllers/test_controller_test.rb:6:in `test_missing'
---------------

原因是ActionController::TestCase 知道路由并且不允许调用不在路由中的操作。但这正是您要测试的。而且,我假设你为什么不使用ActionController::TestCase

在这一点上,我想知道您是否没有测试您不应该测试的东西。也许您应该让 Rails 完成它的工作并相信它的行为是正确的。但是,嘿,我们已经走到了这一步,为什么不直接测试一下 Rails 测试的内容呢。让我们尝试直接调用TestController,而不是通过完整的Rails 应用程序进行调用。如果我们创建一个新的控制器实例,我们可以要求它处理一个动作,即使该动作没有在控制器上定义。但要做到这一点,我们必须深入研究 ActionController 的工作原理并使用AbstractController#process 方法:

require "test_helper"

class TestControllerTest < Minitest::Test
  def test_missing
    assert_raises AbstractController::ActionNotFound do
      TestController.new.process :missing
    end
  end
end

现在我们要求控制器处理一个不存在的动作,并测试控制器的行为是否符合预期。好吗?

【讨论】:

  • “在这一点上,我想知道你是否没有测试你不应该测试的东西。也许你应该让 Rails 完成它的工作并相信它的行为是正确的。” - 在这里。我是牦牛剃须,这只是我遇到的第一个问题。正如你所看到的,我的问题的最后一部分是“不要介意这已经在 Rails 中测试过了。我正努力克服这个问题。”感谢您的冗长解释,它有很大帮助!
猜你喜欢
  • 2016-11-06
  • 2012-06-19
  • 2016-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多