为什么 Rspec No route matches error with Rails?

Why got Rspec No route matches error with Rails?

Rails 个文件

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  # ...
end

app/controllers/api_application_controller.rb

class ApiApplicationController < ActionController::API
  # ...
end

app/controllers/iot_application_controller.rb

class IotApplicationController < ApiApplicationController
  # ...
end

app/controllers/iot/hardwares_controller.rb

class Iot::HardwaresController < IotApplicationController
  def index
    # ...
  end
end

路线

config/routes.rb

Rails.application.routes.draw do
  scope module: :iot do
    resources :hardwares, only: [ :index ], constraints: { format: :json }
  end

  ...
end

检查路线

$ rake routes
Prefix Verb  URI Pattern                    Controller#Action
...
hardwares GET  /hardwares(.:format)         iot/hardwares#index {:format=>:json}

RSpec 文件

spec/controllers/iot/hardwares_controller.rb

require 'rails_helper'

RSpec.describe Iot::HardwaresController, type: :controller do
  describe "GET #index" do
    context "with invalid params" do
      it "renders a JSON response with errors" do
        get :index, params: { "test": 1 }
        expect(response).to have_http_status(:bad_request)
      end
    end
  end
end

错误

$ rspec .

Iot::HardwaresController
  GET #index
    with invalid params
      renders a JSON response with errors (FAILED - 1)

Failures:

  1) Iot::HardwaresController GET #index with invalid params renders a JSON response with errors
     Failure/Error: get :index, params: {"test": 1}

     ActionController::UrlGenerationError:
       No route matches {:action=>"index", :controller=>"iot/hardwares", :test=>1}
     # ./spec/controllers/iot/hardwares_controller.rb:17:in `block (4 levels) in <top (required)>'

Finished in 0.01547 seconds (files took 6.79 seconds to load)
1 example, 1 failure

如何在rspec中正确写路由?

编辑:在查看提供的路由后,我认为主要问题可能是您命名空间控制器而不是路由中的资源,如果这是问题,您将在下面找到解决方案和其他一些建议。


确保在 routes.rb 中正确设置了路由 在你的情况下你应该有类似的东西:

namespace :iot do
  resources :hardwares
end

然后您需要在 RSpec 示例中的 get 请求中指定 path 而不是方法名称:

get "/iot/hardwares", params { "test": 1 }

假设我们有一个 BooksControllerindex 动作。在我们的 routes.rb 文件中有 resources: :books.

只要路由设置正确,您的示例应该可以正常工作:

require 'rails_helper'

RSpec.describe Iot::HardwaresController, type: :controller do
  describe "GET #index" do
    context "with invalid params" do
      it "renders a JSON response with errors" do
        get "/iot/hardwares", params: { "test": 1 }
        expect(response).to have_http_status(:bad_request)
      end
    end
  end
end

为了让它变得更好,您可以提取 let 变量的路径,因为您可能会在更多示例中使用它。像这样: 需要 'rails_helper'

RSpec.describe Iot::HardwaresController, type: :controller do
  describe "GET #index" do
    let(:path) { "/iot/hardwares" }

    context "with invalid params" do
      it "renders a JSON response with errors" do
        get path, params: { "test": 1 }
        expect(response).to have_http_status(:bad_request)
      end
    end
  end
end

您也可以考虑将规格更改为请求规格而不是控制器规格。在 RSpec 的发行说明中,您可以找到:

For new Rails apps: we don't recommend adding the rails-controller-testing gem to your application. The official recommendation of the Rails team and the RSpec core team is to write request specs instead. Request specs allow you to focus on a single controller action, but unlike controller tests involve the router, the middleware stack, and both rack requests and responses. This adds realism to the test that you are writing, and helps avoid many of the issues that are common in controller specs. In Rails 5, request specs are significantly faster than either request or controller specs were in rails 4, thanks to the work by Eileen Uchitelle1 of the Rails Committer Team.

您可以在此处阅读更多相关信息:http://rspec.info/blog/2016/07/rspec-3-5-has-been-released/