Rails 4 routing in rspec for testing PATCH - ruby-on-rails

This is my routes.rb
resources :agreements, param: :customer_number, only: :show, as: 'agreement_from_customer_number'
resources :agreements, only: [:index, :update] do
resources :orders, only: :index
end
This is my AgreementsController
def update
...
end
And this is my rspec test for testing the update action:
before :each do
#agreement = create :agreement
end
it 'updates the correct agreement through the internal API' do
patch agreement_path(#agreement)
end
My application uses ActiveModel, so it has no database. Because of this, the createmethod in the before block simply does the following:
factories/agreement.rb
factory :agreement do
skip_create
id '1'
customer_number '1001'
agreement_id '101'
return_address 'Fakeville, USA'
return_postal_code '2013'
end
The test returns the following error:
ActionController::UrlGenerationError:
No route matches {:id=>#<Agreement:0x007f94ab7b0ce0 #id="1", #agreement_id="101", #customer_number="1001", #return_address="Fakeville, USA", #return_postal_code="2013">} missing required keys: [:id]
And if I change the test to look like this:
it 'updates the correct agreement through the internal API' do
patch agreement_path(#agreement.id)
end
I get the following error:
ActionController::UrlGenerationError:
No route matches {:controller=>"agreements", :action=>"/agreements/1"}
Help :-|

I solved this particular problem by doing the following:
patch :update, agreement: #agreement.attributes
Because the controller expects a hash of values rather than an object literal, this worked :-)

Related

Trouble with accessing controller action within nested route with RSpec

I'm working with the following route:
resources :groups, :only => [:show, :create, :update, :destroy] do
get 'viewmembers' => 'groups#view_members"
end
But when I'm trying to test in RSpec:
describe "GET #viewmembers" do
context "the group members are retrieved" do
before do
#group = FactoryGirl.create :group
..
get :viewmembers, group_id: #group.id
end
it { should respond_with :ok}
# others tests...
end
I get the following error:
Failure/Error: get 'viewmembers', group_id: #group.id
ActionController::UrlGenerationError:
No route matches {:action=>"viewmembers", :controller=>"api/v1/groups", :group_id=>"1"}
Is there a different way I should be formulating the GET request in my Rspec test?
Here is what's listed in rake routes:
GET /api/v1/groups/:group_id/viewmembers(.:format) api/v1/group#viewmembers{:format=>:json}`
If i take the get 'viewmembers'... route OUT of the nested do-end block for the group resource like so:
resources :groups, :only => [:show, :create, :update, :destroy]
get '/groups/viewmembers' => 'groups#view_members'
then the rspec test runs fine, so it has to be a problem with the way I'm formulating the request...

How to set Rails routes on nested resources?

This may seem redundant because similar question has been asked here and here, but I haven't found a solution yet.
I am running an RSpec to test :update for api. When I run RSpec, it shows No Route Matches on my first test. All I need is to have the testing to have_http_status(401) for unauthenticated user. Rails can't figure out the routing.
Here is what the error says:
Failures:
1) PostsController unauthenticated user PUT update returns http unauthenticated
Failure/Error: put :update, topic_id: my_topic.id, post_id: my_post.id, post: {title: my_post.title, body: my_post.body}
ActionController::UrlGenerationError:
No route matches {:action=>"update", :controller=>"posts", :post=>{:title=>"Xdiwbu zuitsom prubmlhd oxmgtkb swphb ukije salhvk.", :body=>"Pjlb ywlzqv igdesqmw oqjgy mrwpye ujierxtn owqxbvt. Wzxu sjcikthg xare tcawzx tedmiqwf lewab. Twkeoun mos ophta fvae krmnsqe. Jxefyo ncd agj ieyanvt uehazwnk mtsi fbsm."}, :post_id=>"1", :topic_id=>"1"}
# ./spec/api/v1/controllers/posts_controller_spec.rb:11:in `block (3 levels) in <top (required)>'
Here is the RSpec (spec/api/v1/controllers/posts_controller_spec.rb)
require 'rails_helper'
RSpec.describe Api::PostsController, type: :controller do
let(:my_user) { create(:user) }
let(:my_topic) { create(:topic) }
let(:my_post) { create(:post, topic: my_topic, user: my_user) }
context "unauthenticated user" do
it "PUT update returns http unauthenticated" do
put :update, topic_id: my_topic.id, post_id: my_post.id, post: {title: my_post.title, body: my_post.body}
expect(response).to have_http_status(401)
end
...
Here are the routes:
namespace :api do
namespace :v1 do
resources :users, only: [:index, :show, :create, :update]
resources :topics, except: [:edit, :new] do
resources :posts, only: [:update, :create, :destroy]
end
end
end
And here is the first part of the test:
class Api::V1::PostsController < Api::V1::BaseController
before_action :authenticate_user, except: [:index, :show]
before_action :authorize_user, except: [:index, :show]
def update
post = Post.find(params[:id])
if post.update_attributes(post_params)
render json: post.to_json, status: 200
else
render json: {error: "Post update failed", status: 400}, status: 400
end
end
...
No matter what I change the RSpec, I can't get it to match the routes. Would you guys mind helping?
Thanks!!
2 things stand out to me.
1: The controller itself is namespaced under Api::V1. The controller in the spec however, is namespaced under just Api. This should be updated to match.
2: If you run rake routes, you'll notice a line like this:
PUT /api/v1/topics/:topic_id/posts/:id(.:format) api/v1/posts#update.
It's important to note the names being given after the : in that message. In here, it's stating that the ID of the topic should be supplied to the controller as topic_id, and the ID of the post should be supplied as just id. If you modify your put statement to be something more like put :update, topic_id: my_topic.id, id: my_post.id, post: {title: my_post.title, body: my_post.body}, it should work.

Rspec fails with ActionController::UrlGenerationError

Rspec fails with ActionController::UrlGenerationError with a URL I would think is valid. I've tried messing with the params of Rspec request, as well as fiddled with the routes.rb, but I'm still missing something.
The weird thing is, it works 100% as expected when testing locally with curl.
Error:
Failure/Error: get :index, {username: #user.username}
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"api/v1/users/devices", :username=>"isac_mayer"}
Relevant code:
spec/api/v1/users/devices_controller_spec.rb
require 'rails_helper'
RSpec.describe Api::V1::Users::DevicesController, type: :controller do
before do
#user = FactoryGirl::create :user
#device = FactoryGirl::create :device
#user.devices << #device
#user.save!
end
describe "GET" do
it "should GET a list of devices of a specific user" do
get :index, {username: #user.username} # <= Fails here, regardless of params. (Using FriendlyId by the way)
# expect..
end
end
end
app/controllers/api/v1/users/devices_controller.rb
class Api::V1::Users::DevicesController < Api::ApiController
respond_to :json
before_action :authenticate, :check_user_approved_developer
def index
respond_with #user.devices.select(:id, :name)
end
end
config/routes.rb
namespace :api, path: '', constraints: {subdomain: 'api'}, defaults: {format: 'json'} do
namespace :v1 do
resources :checkins, only: [:create]
resources :users do
resources :approvals, only: [:create], module: :users
resources :devices, only: [:index, :show], module: :users
end
end
end
Relevant line from rake routes
api_v1_user_devices GET /v1/users/:user_id/devices(.:format) api/v1/users/devices#index {:format=>"json", :subdomain=>"api"}
The index action requires a :user_id parameter, but you haven't supplied one in the params hash. Try:
get :index, user_id: #user.id
The error message is a bit confusing, because you aren't actually supplying a URL; instead you are calling the #get method on the test controller, and passing it a list of arguments, the first one is the action (:index), and the second is the params hash.
Controller specs are unit tests for controller actions, and they expect that the request parameters are correctly specified. Routing is not the responsibility of the controller; if you want to verify that a particular URL is routed to the right controller action (since as you mention, you are using friendly-id), you may want to consider a routing spec.

` No route matches` error for Nested Resources Controller Spec - Rails, Rspec

I keep getting No route matches error for a nested resource #create action. Here is what I got:
routes:
...
resources :users, only: [:show, :create] do
resources :filters, only: [:new,:create]
end
...
controller spec:
...
context 'with valid attributes' do
it "creates new Filter" do
expect{
post :create, {:filter => attributes_for(:filter)}
}.to change(Filter, :count).by(1)
end
...
error:
No route matches {:action=>"create", :controller=>"filters", :filter=>{[long filter hash]}
Because it is nested, you also need to include the ID for the parent object that the filter will belong to.
post :create, user_id: <some_user_id>, {:filter => attributes_for(:filter)}

rspec ActionController::UrlGenerationError for create action in namespaced admin that is nested

I'm getting this error in my controller spec for my create action
No route matches {:action=>"create", :assessment=>{:course_id=>"1", :curriculum_id=>"1"}, :controller=>"admin/assessments"}
Here is my controller spec:
it "sets the flash success" do
set_current_admin
course = Fabricate(:course)
post :create, assessment: { course_id: course.id, curriculum_id: course.curriculum.id }
expect(flash[:success]).not_to be_blank
end
The error occurs on the post :create.. line.
Here is my create action for assessments:
def create
#assessment = Assessment.new(assessment_params.merge!(course_id: Course.find(params[:course_id]).id))
if #assessment.save
flash[:success] = "You have created your assessment."
redirect_to curriculum_course_assessment_path(#assessment.course.curriculum, #assessment.course, #assessment)
else
...
end
end
And, here is my routing for the assessments:
resources :curriculums, only: [:index, :show] do
resources :courses, only: [:show] do
resources :assessments, only: [:show]
namespace :admin do
resources :assessments, only: [:index, :new, :create, :edit, :update]
end
end
end
Here is the line from my rake routes...
POST /curriculums/:curriculum_id/courses/:course_id/admin/assessments(.:format) admin/assessments#create
When I actually run the create action in the browser, it works fine, so I'm guessing that it's a problem with my spec's syntax. Any advice on this would be much appreciated. (I realize that I'm double-nesting by resources here which is not generally the best practice, but I couldn't find any other way to access the curriculums & courses params.)
You're nesting the course_id and curriculum_id parameters inside an assessment hash - in the route, they are not nested.

Resources