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

awe*_*ndt 5 ruby unit-testing minitest ruby-on-rails-4

我正在尝试使用Action Controller错误报告模板在Rails中解决一个奇怪的行为.

对于记录,这是模板中的控制器:

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

  def index
    render text: 'Home'
  end
end
Run Code Online (Sandbox Code Playgroud)

我添加了一个缺少动作的路线:

routes.draw do
  get '/' => 'test#index'
  get '/missing' => 'test#missing'
end
Run Code Online (Sandbox Code Playgroud)

并且我试图断言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
Run Code Online (Sandbox Code Playgroud)

预期行为:绿色测试.

实际行为:

# 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.
Run Code Online (Sandbox Code Playgroud)

因此,基本上,异常被提出,但Minitest声称没有提出任何内容.我在这里难过.

为什么Minitest断言不会AbstractController::ActionNotFound被提出?我已经考虑Rack::Test::Methods过排除get使用线程或其他东西但无法找到任何东西.

我也看了一下实施assert_raises - 没什么了不起的.

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

(不要紧,这已经在Rails中测试了.我正试图通过这个来实现真正的交易.)

blo*_*age 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
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

  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'
---------------
Run Code Online (Sandbox Code Playgroud)

原因是因为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
Run Code Online (Sandbox Code Playgroud)

现在我们要求控制器处理不存在的操作并测试控制器的行为是否符合预期.好极了?