Rails[RSpec]: put :update, no route matches :id => nil - ruby-on-rails

Very strange error. I am writing RSpec tests for a subgroup controller, which is in a many-to-one relationship with group. group accepts nested attributes for subgroup. I used scaffolding, and I've used the tests provided (edited to test). These tests were all working before I started testing for Devise. Now, my tests for updating with invalid parameters are failing.
tests in subgroups_controller_spec.rb
let(:invalid_attributes) {
{name: nil, group_id: nil}
}
context "with invalid params" do
it "assigns the subgroup as #subgroup" do
subgroup = Subgroup.create! valid_attributes
puts "param: #{subgroup.to_param}, id: #{subgroup.id}
put :update, {:id => subgroup.to_param, :subgroup => invalid_attributes}, valid_session
expect(assigns(:subgroup)).to eq(subgroup)
end
it "re-renders the 'edit' template" do
subgroup = Subgroup.create! valid_attributes
put :update, {:id => subgroup.to_param, :subgroup => invalid_attributes}, valid_session
expect(response).to render_template("edit")
end
end
error message:
1) SubgroupsController PUT #update with invalid params assigns the subgroup as #subgroup
Failure/Error: put :update, {:id => subgroup.to_param, :subgroup => invalid_attributes}, valid_session
ActionView::Template::Error:
No route matches {:action=>"show", :controller=>"groups", :id=>nil, :locale=>nil}
But puts "param: #{subgroup.to_param}, id: #{subgroup.id}" prints param: 1, id: 1 so clearly these ids are not nil.
rake routes:
subgroups GET (/:locale)/subgroups(.:format) subgroups#index
POST (/:locale)/subgroups(.:format) subgroups#create
new_subgroup GET (/:locale)/subgroups/new(.:format) subgroups#new
edit_subgroup GET (/:locale)/subgroups/:id/edit(.:format) subgroups#edit
subgroup GET (/:locale)/subgroups/:id(.:format) subgroups#show
PATCH (/:locale)/subgroups/:id(.:format) subgroups#update
PUT (/:locale)/subgroups/:id(.:format) subgroups#update
DELETE (/:locale)/subgroups/:id(.:format) subgroups#destroy
p.s. there are locales listed in the routes but I removed them for easier viewing. I don't think locales is this issue as update needs an :id to update.

The problem is not with your put route, with the in the rendering that it's doing. You're getting a template error and the error specifics show that you're trying to do a show on groups. Check your layout for paths that are used.

Related

Use Patch with rspec tests

I am trying to test patch for devise user info using Rspec, the update url looks like this # PATCH/PUT /api/users/1 but I am getting this error for all the following cases
Error ArgumentError:
wrong number of arguments (given 2, expected 1)
Cases I tried
patch :update, {'id'=> #api_user['user']['id'], 'user' => attributes_for(:normal_user)}
patch :update, 'id'=> #api_user['user']['id'], 'user' => attributes_for(:normal_user)
patch :update, 'id'=> #api_user['user']['id'], :params => {'user' => attributes_for(:normal_user)}
And I tried this
patch :update, :params => {'user' => create(:normal_user)}. # this one has the id within
but gives this error
No route matches {:action=>"update", :controller=>"api/users", :user=>#<User id: 227794695, email: "test11#example.com", created_at: "2020-05-03 08:51:55", updated_at: "2020-05-03 08:51:55", is_admin: nil, first_name: "test", last_name: "test">} which make sence, the url should be update/id
you should not put "update" after patch, since
patch it self will automatically will route to update to user controller
this is the reason error message informed you given 2, expected 1 arguments
here is sample for your reference from update
require 'rails_helper'
RSpec.describe 'User request', type: :request do
it 'should update user email' do
patch "/api/users/#{#api_user['user']['id']}",
params: {
user: {
email: 'new_email_address#gmail.com'
}
},
as: :json
expect(response).to have_http_status(:success)
end
end
here is sample of /config/routes.rb for your reference to update users
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
resources :users, only: [:create, :update, :destroy]
end
end

Rails 4 testing nested resources with multiple URL params

I have been at this for a few hours, still can't figure it out. I have 2 tests on 2 actions on a nested resources controller. requests is the parent resources route, and response is the nested resources route.
These 2 tests give me a no route matches error. Does not make sense. In the first test, it tries to run the update action instead of the edit. Here are my tests:
test "should get edit" do
assert_routing edit_request_response_path(#myresponse.request_id, #myresponse), { :controller => "responses", :action => "edit", :request_id => #myresponse.request_id.to_s, :id => #myresponse.id.to_s }
get :edit, params: { id: #myresponse, request_id: #myresponse.request_id }
assert_response :success
end
test "should update response" do
post :update, :request_id => #myresponse.request_id, response: { body: #myresponse.body, request_id: #myresponse.request_id, status: #myresponse.status, subject: #myresponse.subject, user_id: #myresponse.user_id }
assert_redirected_to response_path(assigns(:response))
end
Here are the errors:
3) Error:
ResponsesControllerTest#test_should_get_edit:
ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"responses", :params=>{:id=>"980190962", :request_id=>"999788447"}}
test/controllers/responses_controller_test.rb:43:in `block in <class:ResponsesControllerTest>'
4) Error:
ResponsesControllerTest#test_should_update_response:
ActionController::UrlGenerationError: No route matches {:action=>"update", :controller=>"responses", :request_id=>"999788447", :response=>{:body=>"This is the body", :request_id=>"999788447", :status=>"draft", :subject=>"This is the subject", :user_id=>"175178709"}}
test/controllers/responses_controller_test.rb:48:in `block in <class:ResponsesControllerTest>'
In this case you might want to use shallow nesting since there is no reason to go through request if you can get to a response by /response/:id.
resources :requests, shallow: true do
resources :response
end
http://guides.rubyonrails.org/routing.html#nested-resources
test "should get edit" do
assert_routing edit_response_path(#myresponse), { :controller => "responses", :action => "edit", :id => #myresponse.id.to_s }
get :edit, params: { id: #myresponse, request_id: #myresponse.request_id }
assert_response :success
end
However naming your business logic objects Request and Response is a big misstake. These are already key concepts in Rails which correspond to the request from the client and the response sent to the client by rails.
You will end up confusing yourself and any poor sucker that has to work on the project. Plus you will end up masking the request and response methods that are pretty important parts of the ActionController API.
Use some other synonym instead.

Rspec no route matches, checking for flash message in controller

I need to test that a flash message comes up when a specific exception is raised. I am getting a no route matches error and have looked through the other topics with "Rspec Controller test, No Routes matches" and am still not sure what is wrong. This is my first time writing an rspec test. Commented out lines are various other attempts to get test to pass.
audit/businesses_controller.rb:
class Audit::BusinessesController < ApplicationController
# PUT /audit/businesses/:id
def update
remove_deleted_account_numbers_in(params)
#business = #jurisdiction.businesses.find(params[:id])
#business.changed_by = current_user
if #business.update_attributes(audit_business_params)
success_message('update')
else
#business.initialize_accounts
#business = #business
render :edit, alert: t('flash.save_failed')
end
rescue ActiveRecord::StatementInvalid => e
if e.message.include? "foreign key constraint"
#business = #jurisdiction.businesses.find(params[:id])
#business.initialize_accounts
#business = #business
render :edit, alert: "This account number is in use and cannot be deleted"
else
raise e
end
end
business_controller_spec.rb:
require "spec_helper"
describe Audit::BusinessesController do
describe "handling ActiveRecord::StatementInvalid" do
before {delete, url = 'audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format)'}
it "should rescue with a flash message" do
# /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format)
# delete, businesses_id: '80776', jurisdiction_id: '795', format: 'html', id: "234769"
expect(page).to have_content("This account number is in use and cannot be deleted")
end
end
end
rake routes:
audit_businesses GET
/audit/jurisdictions/:jurisdiction_id/businesses(.:format)
audit/businesses#
POST /audit/jurisdictions/:jurisdiction_id/businesses(.:format)
audit/businesses#create
new_audit_business GET
/audit/jurisdictions/:jurisdiction_id/businesses/new(.:format)
audit/businesses#new
edit_audit_business GET
/audit/jurisdictions/:jurisdiction_id/businesses/:id/edit(.:format)
audit/businesses#edit
audit_business
GET /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format)
audit/businesses#show
PUT /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format) audit/businesses#update
DELETE /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format) audit/businesses#destroy
Error Message:
Failure/Error: before { delete audit_business_path(:jurisdiction_id, :id, :format) }
ActionController::RoutingError:
No route matches {:controller=>"audit/businesses", :action=>"/audit/jurisdictions/jurisdiction_id/businesses/id.format"}
# ./spec/controllers/businesses_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
When I change the spec to:
describe Audit::BusinessesController do
describe "handling ActiveRecord::StatementInvalid" do
it "should rescue with a flash message" do
put :update, :jurisdiction_id => 1, :id => 1, :format => html
expect(controller).to set_flash[:alert].to(/This account number is in use and cannot be deleted/).now
The error is:
Failure/Error: put :update, :jurisdiction_id => 1, :id => 1, :format => html
NoMethodError:
undefined method `can_update?' for nil:NilClass
routes.rb:
namespace :audit do
root to: 'site#home'
resources :labels, only: ['index', 'destroy'] do
post 'delete_queue', on: :collection
get 'print', on: :collection
get 'print_view', on: :collection
get 'mark_printed', on: :collection
get 'users', on: :collection
end
resources :auditor_goals
resources :contracts
resource :contract_work, only: %w[show update], controller: 'contract_work'
resources :manpower
resource :county_budgets, only: :update do
get 'search'
end
get 'intro_letter_search' => 'files#intro_letter_search'
put 'intro_letter_update' => 'files#intro_letter_update'
get 'search' => 'files#search'
get 'calendar' => 'files#calendar'
get 'autocomplete' => 'files#autocomplete'
get 'direct' => 'files#direct'
get 'size_code_select' => 'businesses#size_code_select'
scope '/jurisdictions/:jurisdiction_id' do
resources :businesses, controller: 'businesses'
resources :files, controller: 'files' do
get 'print', on: :member
end
# get 'page/:page', action: :index, on: :collection
end
end
for rspec test instead write
expect(page).to have_content("flash message")
where "flash message" is the actual message of the error or exception you're trying to make pass. Eg for, flash[:notice] = "An error occurred", you would put have_content("An error occurred")
Hope that helps?!
Justin
#TzokinB - I believe "Capybara::ElementNotFound: Unable to find xpath "/html" typically means the test set is now looking for a view. Have you associated a view for to action in your controller?

Ruby on Rails - Rspec - Controller test - nested route

I got a pretty basic controller test
require 'spec_helper'
describe Admin::OrdersController do
describe "GET #order_detail" do
before :each do
new_admin = FactoryGirl.create(:admin)
sign_in new_admin
#storefront = FactoryGirl.create(:storefront)
#order = FactoryGirl.create(:order)
end
it "assigns the requested order to #order" do
get :order_detail, { :storefront_id => #storefront.id, :order_id => #order.id }
assigns(:order).should eq(#order)
end
it "renders the :show template" do
get :order_detail, {:storefront_id => #storefront.id, :order_id => #order.id}
response.should render_template :order_detail
end
end
end
Which gets me the following error for both actions:
ActionController::RoutingError:
No route matches {:storefront_id=>"14", :order_id=>"1", :controller=>"admin/orders", :action=>"order_detail"}
From the routes.rb:
resources :storefronts do
resources :orders do
member do
get :order_detail
end
end
end
I thought
get :order_detail, { :storefront_id => #storefront.id, :order_id => #order.id }
would be the right way to generate the route but unfortunately it's not.
You can see the routes generated by Rails using rake routes or bundle exec rake routes in the application root. I did the same resource set-up in a brand new rails app, and rake routes output is as follows (only for order detail route):
order_detail_storefront_order GET /storefronts/:storefront_id/orders/:id/order_detail(.:format) orders#order_detail
As you can see, rails is expecting :id and not :order_id. Try changing :order_id to :id as the parameter key in your spec.

Testing nested resource controller with rspec in Rails 3.1

I am trying to make a test for a controller for a nested resource.
The nesting is like this in the routes.rb
resources :cars, :only => [:index, :destroy, :show] do
resources :car_subscriptions, :only => [:new, :create], :as => :follow_subscriptions
end
I'm trying to test the create action most specifically:
describe CarSubscriptionsController do
def valid_attributes
{:car_id => '1', :user_id => '2'}
end
describe "POST create" do
describe "with valid params" do
it "creates a new CarSubscription" do
expect {
post :create, :car_id => 1, :car_subscription => valid_attributes
}.to change(CarSubscription, :count).by(1)
end
it "assigns a newly created car_subscription as #car_subscription" do
post :create, :car_subscription => valid_attributes
assigns(:car_subscription).should be_a(CarSubscription)
assigns(:car_subscription).should be_persisted
end
it "redirects to the created car_subscription" do
post :create, :car_subscription => valid_attributes
response.should redirect_to(CarSubscription.last)
end
end
end
end
It's actually a part of the scaffold generated by rails script. And I only modified the valid_attributes and the post in the first 'it'
And the output is this:
1) CarSubscriptionsController POST create with valid params creates a new CarSubscription
Failure/Error: post :create, :car_id => 1, :car_subscription => valid_attributes
ActionController::RoutingError:
No route matches {:car_id=>"1", :car_subscription=>{:car_id=>"1", :user_id=>"2"}, :controller=>"car_subscriptions", :action=>"create"}
# ./spec/controllers/car_subscriptions_controller_spec.rb:34:in `block (5 levels) in <top (required)>'
# ./spec/controllers/car_subscriptions_controller_spec.rb:33:in `block (4 levels) in <top (required)>'
It's the same error for all 'it's.
I've tried removing the :as => :following_subscriptions from the routes.rb file but the same problem.
I have actually split up the resources of car_subscriptions so index and destroy are in not nested, and create and new are nested in :cars
I don't want to use hard coded paths like in this answer but if it is the only way, I can give it a try:
{ :post => "/forum_topics/1/forum_sub_topics" }.should route_to(:controller => "forum_sub_topics", :action => "create", :forum_topic_id => 1)
EDIT
Oh, and my rake routes looks like this:
car_follow_subscriptions_da POST /biler/:car_id/car_subscriptions(.:format) {:action=>"create", :controller=>"car_subscriptions", :locale=>"da"}
From what rake routes provides, I guess you should replace:
post :create, :car_id => 1, :car_subscription => valid_attributes
with:
post :create, :car_id => 1, :car_subscription => valid_attributes, :locale => "da"

Resources