Beginner Professional
Beginner Professional

Reputation: 347

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

What I want to solve

I want the Rspec patch or put test to succeed. I also tested PostsContoroller before this, and I am puzzled because I did not get the same error when testing PostsContoroller.

Error

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

Code

FactoryBot

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

Spec

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

Routes

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

Upvotes: 1

Views: 249

Answers (1)

max
max

Reputation: 101831

The use of controller specs is discouraged by both the Rails and RSpec teams and has been for a very long time now. You should be writing a request spec instead which sends real HTTP requests.

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

Another problem is that you're generating IDs in your factory. Do not do this ever. When you're actually persisting records the database will automatically assign ids. When you use build_stubbed FactoryBot will create a mock id. Using a sequence to generate IDs invites bad practices such as hardcoding ids into a spec and will only cause you headaches.

If you really want to salvage that controller spec the routing error is caused by the fact that you're missing an the ID parameter - since you're calling it as patch :update, params: { post: post_params } the id parameter is buried in params[:post][:id]. So you want patch :update, params: { id: post.id, post: post_params } I don't recommend this though - get with the program and write future proof tests instead that won't let all the bugs slip though.

Upvotes: 2

Related Questions