In RSpec, running a patch or put test will result in an ActionController::UrlGenerationError: No route matches error

In RSpec, running a patch or put test will result in an ActionController::UrlGenerationError: No route matches error

我想解决的问题

我希望 Rspec 补丁或测试成功。 之前我也测试过PostsContoroller,很纳闷,因为我在测试PostsContoroller的时候没有报同样的错误。

错误

Failures:

  1) Api::V1::PostItemsController update Update Content
     Failure/Error: patch :update, params: { post: post_params }

     ActionController::UrlGenerationError:
       No route matches {:action=>"update", :controller=>"api/v1/post_items", :post=>{:id=>1, :content=>"Update-Content", :status=>false, :post_id=>1}}
     # ./spec/controllers/post_items_spec.rb:11:in `block (3 levels) in <main>'

Finished in 0.35529 seconds (files took 5.58 seconds to load)
5 examples, 1 failure

代码

工厂机器人

book.rb


FactoryBot.define do
  factory :book, class: Post do
    sequence(:id) { |n| n}
    sequence(:title) { |n| "title#{n}" }
    sequence(:author) { |n| "author#{n}" }
    sequence(:image) { |n| "image#{n}"}
  end
end

content.rb


FactoryBot.define do
  factory :content, class: PostItem do
    sequence(:id) { |n| n }
    sequence(:content) { |n| "list#{n}"}
    sequence(:status) { false }  
  end
end

规格

post_items_spec.rb

require 'rails_helper'

RSpec.describe Api::V1::PostItemsController, type: :controller do
  
  describe 'update' do
    it 'Update Content' do

      book = create(:book)
      content = create(:content, post_id: book.id)
      post_params =  { id: content.id, content: 'Update-Content', status: false, post_id: book.id }

      patch :update, params: { post: post_params }
      json = JSON.parse(response.body)
      expect(response.status).to eq(200)
      expect(json['Update-Content']).to eq('Update-content')

    end
  end
end

路线

**Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :posts
      resources :post_items
      end
   end
end

Rails 和 RSpec 团队都不鼓励使用控制器规范,并且已经存在很长时间了。您应该编写请求规范,而不是发送真正的 HTTP 请求。

RSpec.describe 'Api V1 Post items', type: :request do
  let(:book) { create(:book) }

  describe "PATCH /api/v1/books" do
    context "with valid parameters" do
      subject do
        patch api_v1_post_item_path(book),
                 params: { content: 'Update-Content' }
      end
   
      it { should be_successful } 
      it "updates the content" do
        # refresh the record from the db
        expect { book.reload }.to change(book, :title).to('Update-Content')
      end
      it "includes the updated entity in the response body" do
        expect(response.parsed_body['content']).to eq 'Update-Content'
      end
    end

    # @todo write specs with invalid parameters 
    # @todo write specs for authentication and authorization
  end
end

另一个问题是您在工厂中生成 ID。永远不要这样做。当您实际保留记录时,数据库将自动分配 ID。当您使用 build_stubbed 时,FactoryBot 将创建一个模拟 ID。使用序列生成 ID 会带来不良做法,例如将 ID 硬编码到规范中,只会让您头疼。

如果您真的想挽救该控制器规范,路由错误是由于您缺少 ID 参数这一事实引起的 - 因为您将其称为 patch :update, params: { post: post_params },id 参数被埋在params[:post][:id]。所以你想要 patch :update, params: { id: post.id, post: post_params } 虽然我不推荐这个 - 开始使用程序并编写未来的证明测试,而不是让所有的错误都溜走。