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
Related
I am building an API and, upon writing the tests, I run into a strange UrlGenerator Error.
I have an API on version one and this is my Users controller.
class Api::V1::UsersController < ApplicationController
respond_to :json
def show
respond_with User.find(params[:id])
end
end
Here is the spec for that users controller
require 'rails_helper'
RSpec.describe Api::V1::UsersController, type: :controller do
before(:each) { request.headers['Accept'] = "application/vnd.marketplace.v1" }
describe "GET #show" do
before(:each) do
#user = FactoryBot.create :user
get :show, format: :json
end
it "returns the information about a reporter on a hash" do
user_response = JSON.parse(response.body, symbolize_names: true)
expect(user_response[:email]).to eql #user.email
end
it { should respond_with 200 }
end
end
When I run this spec I get the following error message: `Failure/Error: get :show, format: :json
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"api/v1/users", :format=>:json}`
I have only one route for my API:
api_user GET /users/:id(.:format) api/v1/users#show {:subdomain=>"api", :format=>:json}
Does anybody know why I would be getting this error? It seems to me that, based on the route returned from the api routes list, this should be working. My routes.rb file is listed below:
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, :only => [:show]
end
end
The problem is that the show route that you have defined requires an :id parameter, but the call for get :show from the test does not send it.
From Rspec, you can send the id with something like:
get :show, params: { id: #user.id }, format: :json
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.
When running my controller test, I get this error:
NoMethodError:
undefined method `api_challenge_url' for #<Api::V1::ChallengesController:0x007f829b233460>
Which, is in fact, not a route that exists. My routes file looks like this:
namespace :api, { format: :json, constraints: { subdomain: 'api' }, path: '/'} do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, only: [:show, :create, :update, :destroy] do
resources :challenges, only: [:create, :show]
end
end
end
And my controller test looks like this:
RSpec.describe Api::V1::ChallengesController, type: :controller do
describe "POST #create" do
context "when successfully created" do
before(:each) do
#user = FactoryGirl.create(:user)
#challenge_attributes = FactoryGirl.attributes_for(:challenge)
post :create, user_id: #user.id, challenge: #challenge_attributes, format: :json
end
it "should render the JSON for the created challenge" do
challenge_response = JSON.parse(response.body, symbolize_names: true)
expect challenge_response[:description].to eql #challenge_attributes["description"]
expect challenge_response[:title].to eql #challenge_attributes["title"]
end
end
end
end
But for the life of me, I can't why it's calling the wrong route name. The output of the relevant part of the rake routes looks like this:
api_user_challenges POST /users/:user_id/challenges(.:format) api/v1/challenges#create {:subdomain=>"api"}
I've tried a few different formats in the post method, is there some idiomatic way of doing this that I'm missing?
Try adding some configuration to include url helpers into your test suite:
RSpec.configure do |c|
c.include Rails.application.routes.url_helpers
# Other configurations ...
end
And if you prefer using xxx_url over xxx_path, remember to config action_controller.default_url_options in your config/environments/test.rb, for example:
config.action_controller.default_url_options = {
host: 'www.mysite.org',
protocol: 'https'
}
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.
I'm having controller test with devise, but it always fail because assigns always return nil, please help to find where the problem is, thanks a million!
posts_controller_spec.rb:
RSpec.describe PostsController, :type => :controller do
describe "with valid session" do
describe "GET index" do
it "assigns all posts as #posts" do
sign_in :admin, #user
post = create(:post)
get :index, {}
expect(assigns(:posts)).to eq([post])
end
end
end
...
end
posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
...
end
I've included devise test helpers in spec/rails_helper.rb
config.include Devise::TestHelpers, type: :controller
In my case, post is scoped under admin, not sure if that makes difference (functional test doesn't get through routes?), so I just include my routes.rb here
routes.rb:
Rails.application.routes.draw do
root to: 'home#index'
get 'admin', to: 'admin#index'
devise_for :users
scope '/admin' do
resources :posts
end
end
And finally, the output from rspec:
1) PostsController with valid session GET index assigns all posts as #posts
Failure/Error: expect(assigns(:posts)).to eq([post])
expected: [#<Post id: 57, title: "MyText", body: "MyText", image_url: "MyString", created_at: "2014-09-02 14:36:01", updated_at: "2014-09-02 14:36:01", user_id: 1>]
got: nil
(compared using ==)
# ./spec/controllers/posts_controller_spec.rb:53:in `block (4 levels) in <top (required)>'
I've read this thread rspec test of my controller returns nil (+factory girl) , and followed the suggestion to change get :index to controller.index . The suggestion is that if that passes the test then it's a routing problem. It does pass the test, but I still have no idea where the routing problem is, and why the get :index is not working...
It's just a small mistake: create an user before using devise sign_in
RSpec.describe PostsController, :type => :controller do
describe "with valid session" do
let (:user) { create(:user) }
describe "GET index" do
it "assigns all posts as #posts" do
sign_in user
post = create(:post)
get :index, {}
expect(assigns(:posts)).to eq([post])
end
...
end
end
end