为什么Minitest的assert_raises在这种情况下不能按预期工作?

时间:2014-04-24 08:47:14

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

我正试图使用​​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中测试了。我正试图通过这个实际交易。)

1 个答案:

答案 0 :(得分:12)

这不是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测试的东西。我们不是通过完整的Rails应用程序进行调用,而是尝试直接调用TestController。如果我们创建一个新的控制器实例,我们可以要求它处理一个动作,即使该动作没有在控制器上定义。但要做到这一点,我们必须深入了解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

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