functional/controller 测试中没有路由匹配

No route matches in functional/controller test

我使用 Minitest::Rails 和 Rails 进行了以下控制器测试 4. 当我 运行 它时,我得到一个错误: ActionController::UrlGenerationError: No route matches {:action=>"/", :controller=>"foo"} 错误,尽管它被定义了.

这一切的重点是测试 ApplicationController 上的方法(FooController 仅存在于此测试中,不是问题的占位符)。

class FooController < ApplicationController
  def index
    render nothing: true
  end
end

class FooControllerTest < ActionController::TestCase
  it 'does something' do
    with_routing do |set|
      set.draw do
        root to: 'foo#index', via: :get
      end

      root_path.must_equal '/' #=> 
      get(root_path).must_be true #=> No route matches error
    end
  end
end

在 Whosebug 和其他地方有很多类似的问题,但它们都是指路由段被遗漏的问题(例如 PUT 上没有指定 ID)。然而,这是一个没有参数的简单 GET。

如果路由的组合方式不同,我会得到相同的结果,所以我认为这不是 root_path 位(例如 controller :foo { get 'test/index' => :index })。

The whole point of this is to test behavior inherited by the ApplicationController.

除了 FooController 继承自 ApplicationController 之外,您的问题中没有与 ApplicationController 相关的行为。并且由于 FooController 中没有其他与 Application Controller 中的内容相关的行为需要测试...

你可以测试这个

class FooController < ApplicationController
  def index
    render nothing: true
  end
end

有了这个

describe FooController, type: :controller do
  describe '#index' do
    it 'does not render a template' do
      get :index
      expect(response).to render_template(nil)
    end
  end
end

我搜索了你提供的信息。我在 rspec-rails gem 中发现了一个未解决的问题。然而 gem 在这里并不重要,但从根本上说,他们说的是它的上下文问题。当您调用 with_routing 时,它不会在正确的上下文中执行,因此会给出 No Route matches 错误。

为了解决问题,我在本地尝试了不同的解决方案。这是我试过的

require 'rails_helper'

RSpec.describe FooController, type: :controller do
  it 'does something' do

    Rails.application.routes.draw do
      root to: 'foo#index', via: :get
    end

   expect(get: root_path).to route_to("foo#index")

  end
end

在上面的代码中,主要的问题是它覆盖了现有的路由。但是我们可以使用 Rails.application.reload_routes! 方法重现路由。

希望对你有所帮助!!

更新

我试图理解你最后的评论并深入研究 get 方法。当我们调用 get 方法时,它接受我们正在测试的控制器的动作参数。在我们的例子中,当我们执行 get(root_path) 时,它会尝试查找不存在的 foo#/,因此不会给出路由匹配错误。

因为我们的主要目标是检查 root_path 路由是否正确生成,我们需要使用方法 assert_routing 来检查它。这是我的测试方法并且有效 assert_routing root_path , controller: "foo", action: "index"

完整代码:

require 'test_helper'

class FooControllerTest < ActionController::TestCase
  it 'does something' do
    with_routing do |set|
      set.draw do
        root to: 'foo#index', via: :get
      end

      root_path.must_equal '/' #=> true
      assert_routing root_path , controller: "foo", action: "index" #=> true
      get :index
      response.body.must_equal ""
    end
  end
end

我从官方文档中看到了一些东西:http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html

如果您检查 ActionController::TestCase#get 方法的来源,它需要操作名称,例如:index, :create, '编辑, 'create'

如果您在 #get 方法上传递 root_path,绝对会引发错误,因为 root_path 方法 returns '/'。 我刚刚检查了将 :/ 方法添加到 FooController

class FooController
  def index
  end

  def /
  end
end

Rails.application.routes.draw do
  root 'foo#index'
  get 'foobar' => 'foo#/'
end

当我访问http://localhost:3000/foobar时,rails给了我 AbstractController::ActionNotFound (The action '/' could not be found for FooController):回复

我认为'/'是不允许对rails进行操作的,我没有进一步研究,因为我认为这是非常合理的。

你可以写 assert_routing '/', controller: "foo", action: "index" 对于当前测试,那么您可以编写集成测试来检查 root_path 和其他功能。

以下是我上面提到的一些方法的源代码:(我使用rails 4.2.3版本来测试这个有趣的问题)

action_controller/test_case.rb

  # Simulate a GET request with the given parameters.
  #
  # - +action+: The controller action to call.
  # - +parameters+: The HTTP parameters that you want to pass. This may
  #   be +nil+, a hash, or a string that is appropriately encoded
  #   (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
  # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
  #
  # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
  # +post+, +patch+, +put+, +delete+, and +head+.
  #
  # Note that the request method is not verified. The different methods are
  # available to make the tests more expressive.
  def get(action, *args)
    process(action, "GET", *args)
  end


  # Simulate a HTTP request to +action+ by specifying request method,
  # parameters and set/volley the response.
  #
  # - +action+: The controller action to call.
  # - +http_method+: Request method used to send the http request. Possible values
  #   are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+.
  # - +parameters+: The HTTP parameters. This may be +nil+, a hash, or a
  #   string that is appropriately encoded (+application/x-www-form-urlencoded+
  #   or +multipart/form-data+).
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
  # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
  #
  # Example calling +create+ action and sending two params:
  #
  #   process :create, 'POST', user: { name: 'Gaurish Sharma', email: 'user@example.com' }
  #
  # Example sending parameters, +nil+ session and setting a flash message:
  #
  #   process :view, 'GET', { id: 7 }, nil, { notice: 'This is flash message' }
  #
  # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
  # prefer using #get, #post, #patch, #put, #delete and #head methods
  # respectively which will make tests more expressive.
  #
  # Note that the request method is not verified.
  def process(action, http_method = 'GET', *args)
    # ..... 
  end

这个问题的解决方案比预期的要简单得多:

# change this:
get(root_path)

# to this
get(:index)

with_routing 方法可以很好地定义此上下文中的路径。