Rspec testing devise failing because assigns always nil - ruby-on-rails

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

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 Api test UrlGenerationError

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

Rspec controller action not found

test is failing becasue it says the action does not exist, when it clearly does. Is it becasue it is a nested route? Any thoughts?
Update:
I moved resources :orders outside of the nested route and tests passed. So it has something to do with it being nested.
OrderController
def index
if current_printer
#orders = Order.all
#printer = Printer.find(params[:printer_id])
end
if current_user
#orders = Order.where(user_id: params[:user_id])
end
end
OrdersController Spec
require 'rails_helper'
RSpec.describe OrdersController, :type => :controller do
describe "unauthorized user" do
before :each do
# This simulates an anonymous user
login_with_user nil
binding.pry
end
it "should be redirected back to new user session" do
get :index
expect( response ).to redirect_to( new_user_session_path )
end
end
end
Routes
resources :users, only: [:index, :show] do
resources :orders
end
Error
Failures:
1) OrdersController unauthorized user should be redirected back to new user session
Failure/Error: get :index
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"orders"}
When testing controllers that have nested routes you must pass in a hash of the url params.
for example my routes looked like this
user_orders GET /users/:user_id/orders(.:format) orders#index
so in my test I passed in a hash with user_id
get :index, { user_id: 1 }
Tests passing :)

Undefined method route_name in Rspec controller test

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'
}

ActionController::UrlGenerationError - with route defined and action in controller, still getting error for no route

I'm getting the following error in RSpec when running my schools_controller_spec.rb test:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"schools"}
What's puzzling me is that I have the routes configured, and the action defined in the appropriate controller. I'm not getting this error for other tests in the spec, such as 'GET #index', etc. Running Rails 4.2 with RSpec/Capybara.
Here's the routes.rb:
Rails.application.routes.draw do
root to: 'pages#home', id: 'home'
resources :users
resources :schools
resource :session, only: [:new, :create, :destroy]
match '/home', to: 'pages#home', via: 'get', as: 'home_page'
end
rake routes returns:
schools GET /schools(.:format) schools#index
POST /schools(.:format) schools#create
new_school GET /schools/new(.:format) schools#new
edit_school GET /schools/:id/edit(.:format) schools#edit
school GET /schools/:id(.:format) schools#show
PATCH /schools/:id(.:format) schools#update
PUT /schools/:id(.:format) schools#update
DELETE /schools/:id(.:format) schools#destroy
There's the route defined on the fifth line, as schools#show.
The schools_controller.rb:
class SchoolsController < ApplicationController
before_action :require_signin
before_filter :admin_only, except: :index, :show
def index
#schools = School.all
end
def show
# code pending
end
private
def admin_only
unless current_user.admin?
redirect_to :back, alert: "Access denied."
end
end
end
The link to the individual school seems to be properly defined in the view helper (_school.html.haml):
%li#schools
= link_to school.name, school
= school.short_name
= school.city
= school.state
and looking at the front-end HTML confirms it's working correctly. I can see, for example: Community College of the Air Force. When I click that link the page shows the following in the debug dump:
--- !ruby/hash:ActionController::Parameters
controller: schools
action: show
id: '1'
Finally, for good measure, here's the spec file (schools_controller_spec.rb):
require 'rails_helper'
describe SchoolsController, type: :controller do
# specs omitted for other actions
describe 'GET #show' do
context "when not signed in" do
it "returns a 302 redirect code" do
get :show
expect(response.status).to eq 302
end
it "redirects to the signin page" do
get :show
expect(response).to redirect_to new_session_path
end
end
context "when signed in as user" do
before :each do
#user = double(:user)
allow(controller).to receive(:current_user).and_return #user
#school = create(:school)
end
it "assigns the school to the #school variable" do
get :show
expect(assigns(:school)).to eq #school
end
end
end
end
The route appears in rake routes. The method is defined in the appropriate controller. There don't appear to be any silly naming errors (e.g. plural/singular). The spec doesn't appear to have any issues routing GET #index or other routes for example. Everything works exactly as expected in the browser.
So why do I keep getting the "no route matches" error when I run my controller spec?
This is because the show action is expecting an id which you currently aren't passing. Replace:
get :show
With this:
get :show, id: school.id
The above assumes you have a school variable, perhaps a let in a before block?
let(:school) { create(:school) }

Resources