rails 4 ActionController::UrlGenerationError no match route with rspec - ruby-on-rails

I am currently using rspec for unit test, I have traveled quite a doc and I did not find the answer.
I think I just used.
when I test my controller it tells me that is not my way and yet when I test in normal mode so everything works my way to work.
here Controller
#app/controller/app/email_contacts_controller.rb
class App::EmailContactsController < App::ApplicationController
def create
end
end
here rspec controller
require 'rails_helper'
describe App::EmailContactsController, type: :controller do
login_lawyer_partner
let(:contact) { create(:contact) }
let(:lawyer) { controller.current_user }
let(:order) { create :order, contact_id: contact.id, lawyer_id: lawyer.id}
describe 'POST email_contacts' do
before :each do
xhr :post :create, id: order.id, mail: { subject: 'Mail Subject', reply_to: 'toto titi <tooto#titi.com', message: 'This is a long message which length has to be greater than seventy characters, so it passes' }
order.reload
end
it { expect(Order.all).to eq [order] }
it { is_expected.to respond_with :success }
it { expect(order.comments.count).to eq 1 }
end
end
puts params => "controller"=>"app/email_contacts", "action"=>"create"
And
rake route
app_order_email_contacts_path POST /app/orders/:order_id/email_contacts(.:format) app/email_contacts#create
Yet the url is working well, so I think I forgot something or keyword, but I do not see what could miss
Thank you

I think I may have spotted the error. When using a xhr in controller tests there is a comma after the verb symbol. You are missing one after the :post.
Minimally rewritten:
xhr :post, :create, id: order.id, mail: { ...

Related

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
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.

test update request with RSpec failed

I have two problems when I try to test the update action with RSpec, here is the controller file:
#volunteers_controller.rb
module Api
module V1
class VolunteersController < ApplicationController
before_action :find_volunteer, only: %i[show update destroy]
def update
#volunteer.update!(volunteer_params)
head :no_content
end
private
def find_volunteer
#volunteer = Volunteer.find_by!(id: params[:id])
end
def volunteer_params
params.require(:volunteer).permit(:image_url, :name, :job_desc)
end
end
end
end
Here is the test file:
require 'rails_helper'
RSpec.describe Api::V1::VolunteersController, type: :request do
...
describe '#update' do
let(:volunteer) { Volunteer.create!( :image_url=>"first.jpg", :name=>"test1", :job_desc=>"description") }
let(:params){
{:volunteer => {
"image_url"=>"new.jpg",
"name"=>"test1",
"job_desc"=>"description"
}
}
}
it 'updates a certain volunteer' do
patch :patch, :params => params #failed, bad URL
expect(volunteer.image_url).to eq("new.jpg") #failed, still return 'first.jpg'
end
it 'returns a no_content header' do
patch "http://localhost:3000/api/v1/volunteers/#{volunteer.id}", :params => params
expect(response).to have_http_status "204"
end
end
end
private
def json_parse(string)
if string.class==String
json = JSON.parse(string)
end
json
end
So my questions are:
when try to write the URL like this: patch :patch, :params => params, I got the following error:
Api::V1::VolunteersController#update updates a certain volunteer
Failure/Error: patch :patch, :params => params
URI::InvalidURIError:
bad URI(is not URI?): "http://www.example.com:80patch"
How can I change the URL to: "http://localhost:3000/api/v1/volunteers/#{volunteer.id}"?
I manually test the update action, putting a binding.pry in the update action, it does update volunteer subject, however, when it goes back to the test, it shows that it doesn't not get updated, why is that?
Thank you!!
The first problem is really your update method itself and its complete lack of error handling and meaningful feedback to the client. update! will raise ActiveRecord::RecordInvalid if the input is invalid - which is not rescued at all in your controller. And exceptions should no be used for normal code flow - invalid input is not really an exceptional event.
Instead you should rewrite your controller so that it checks if the update is performed and returns the appropriate response:
def update
if #volunteer.update(volunteer_params)
head :no_content
else
head :unprocessable_entity
end
end
As for the spec itself you're mixing up controller specs and request specs. While they look somewhat similar the key difference is that a request spec sends actual HTTP requests your rails server while a controller spec stubs the actual request and passes it to an instance of the controller under test.
In a controller spec you could write:
patch :update, params: { ... }
Because its actually calling the update method on an instance of the controller. But of course:
patch :patch, :params => params #failed, bad URL
Will not work in request spec since its not a valid URL and request specs send actual HTTP requests. Note that you should pass relative URLs and not absolute URLs as the test server may run on a different port then the dev server
# Bad
patch "http://localhost:3000/api/v1/volunteers/#{volunteer.id}", :params => params
# Good
patch "/api/v1/volunteers/#{volunteer.id}", params: params
ActiveRecord models are not "live reloading" - the representation in memory will not automatically be updated when the values in the database are updated. You need to manaully reload the record for that to happen:
it 'updates a certain volunteer' do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
volunteer.reload
expect(volunteer.image_url).to eq("new.jpg")
end
Altogether your spec should actually look something like:
# Describe the endpoint - not the controller implmentation
RSpec.describe "V1 Volunteers API", type: :request do
describe 'PATCH /api/v1/volunteers/:id' do
# use do ... end if the expression does not fit on one line
let(:volunteer) do
# enough with the hashrockets already!
Volunteer.create!(
image_url: "first.jpg",
name: "test1",
job_desc: "description"
)
end
context "with invalid parameters" do
# some set of failing parameters
let(:params) do
{
volunteer: {
name: ""
}
}
end
it "returns unproccessable entity" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect(resonse).to have_http_status :unproccessable_entity
end
it "does not update the volunteer" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect { volunteer.reload }.to_not change(volunteer, :name).to("")
end
end
context "with valid parameters" do
# some set of failing parameters
let(:params) do
{
volunteer: {
image_url: "new.jpg",
name: "test1",
job_desc: "description"
}
}
end
it "returns no content" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect(resonse).to have_http_status :no_content
end
it "updates the volunteer" do
patch "/api/v1/volunteers/#{volunteer.id}", params: params
expect { volunteer.reload }.to change(volunteer, :image_url)
.to("new.jpg")
end
end
end
end

Testing strong parameters with shoulda_matchers

I'm having a hard time trying to figure out how to test the strong parameters with rspec and shoulda_matchers.
No matter what I've tried, I always get the message: RSpec::Expectations::ExpectationNotMetError: Expected POST #create to restrict parameters on :language_name to :name,
but it did not restrict any parameters.
This is my controller:
class LanguageNamesController < CrudController
inherit_resources
actions :all, :except => [:show]
private
def language_name_params
params.require(:language_name).permit(:name)
end
end
This is the test:
require 'rails_helper'
RSpec.describe LanguageNamesController, type: :controller do
describe "parameters" do
it do
lm_params = {
language_name: {
name: 'John'
}
}
expect(subject).to permit(:name).for(:create, params: { params: lm_params }).on(:language_name)
end
end
end
Also, I've tried this and got the same result:
it do
subject.params = ActionController::Parameters.new(language_name: {foo: 'bar', name: 'baz'})
expect(subject).to permit(:name).for(:create).on(:language_name)
end
if I try like this, I got ArgumentError: unknown keyword: language_name:
it do
lm_params = {
language_name: {
name: 'John'
}
}
expect(subject).to permit(:name).for(:create, params: lm_params).on(:language_name)
end
I'm using:
ruby 2.5.1
rails 5.1.6
shoulda-matchers 3.1.2
Anyone know how to solve this?
Thank you!
This is my workaround to test it:
it do
faker_name = Faker::Name.name
params = {
language_name: {name: faker_name, other_attribute: "other value"},
extra: {extra: 1}
}
get :index, params: params
expect(subject.send(:language_name_params)).to eq({"name"=> faker_name})
end
You do not need to create lm_params. Shoulda matchers are going to mock up the params object for you.
If you only need to test the permissions you can do should permit(:name).for(:create)
If you need to check for a value on a permitted parameter: should permit(:name).for(:create, params: {name: 'John'})
There are good examples in the documentation for strong_parameters_matcher.rb
EDIT: Another good source of documentation is the rubydoc for shoulda matchers.

Rails 5 rspec controller test return nil result

Here I have this problem while writing test for this route
close_offer POST (/:locale)/offers/:code/close(.:format) offers#close {:locale=>/en|vi/}
This is the test that I wrote:
RSpec.describe OffersController, type: :controller do
let(:access_token) { extract_access_token_from_vcr_cassette }
describe 'POST #close' do
describe 'successful response' do
before(:each) do
VCR.use_cassette('offers/successful_close_offer') do
post :close, params: { code: 5742, access_token: access_token }, xhr: true
end
end
it 'returns success? = true' do
expect(assigns(:result)).not_to be_nil
end
end
end
end
Somehow, the assigns(:result) always return nil. Although it works perfectly in tests in other routes. The real code also works, only the test failed. It seems that no operation has run, no vcr record, so I suspect that this line post :close, params: { code: 5742, access_token: access_token }, xhr: true is wrong. Anyone has a hint?
Yes, I used to face the same situation. Let's check you authentication. Somehow, you couldn't sign in so that the route is not like you expected, no result returned.

RSpec testing: post multiple params to methods

I am using RSpec to test my Rails 4 application and I want to post a "multiple select" param. The params method is like this:
def general_mailing_params
params.require(:mailing).permit({:receivers => []}, :subject, :content)
end
As you can see the receivers param is a multiple select, how can I post this sort of params in RSpec test?
In RSpec controller and request specs you can simply pass arrays and hashes to create any given params hash.
Controller (functional) spec:
require 'rails_helper'
describe MailingsController do
let!(:receiver) { create(:receiver) }
describe 'POST :create' do
it "has the correct receivers" do
post :create, { mailing: { receivers: [receiver.id] } }
expect(Post.last.receivers).to eq [receiver]
end
end
end
Request (integration) spec:
require 'rails_helper'
describe 'Mailings' do
let!(:receiver) { create(:receiver) }
describe 'POST /mailings' do
it "has the correct receivers" do
post '/mailings', { mailing: { receivers: [receiver.id] } }
expect(Post.last.receivers).to eq [receiver]
end
end
end
Note however if you are using the rails collection helpers such as collection_checkboxes properly the param key should be receiver_ids.

Resources