Undefined method route_name in Rspec controller test - ruby-on-rails

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

Related

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

Functional test for scoped controller can't find the right route

My routes.rb is :
Rails.application.routes.draw do
namespace :api, path: "/", constraint: { subdomain: "api" }, defaults: { format: :json } do
scope module: :v1 do
resources :users, only: [:show]
end
end
end
My users_controller.rb is :
class Api::V1::UsersController < ApplicationController
respond_to :json
def show
respond_with User.find(params[:id])
end
end
My test is :
require 'test_helper'
class Api::V1::UsersControllerTest < ActionController::TestCase
test '#show displays the specific user' do
#user = FactoryGirl.create(:user)
get :show, id: #user.id, format: :json
assert_response :success
end
end
Why does this test give the following error:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"api/v1/users", :format=>:json, :id=>1}
I just needed to add the constraint to the test:
get :show, id: #user.id, format: :json, :constraint=>{:subdomain=>"api"}

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.

Rspec testing devise failing because assigns always nil

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

Rails DESTROY action on nested resource without ID

I have the following resource set up in my routes.rb file:
namespace :api, path: '/', defaults: { format: 'json' }, constraints: { subdomain: 'api' } do
namespace :v1 do
resources :yums do
resources :likes, only: [:create, :destroy, :index] do
match '/', to: 'likes#destroy', via: 'delete'
end
end
end
end
I also wrote a spec to test my requests:
describe "Like API requests" do
before (:each) do
host! 'api.example.com'
#user = FactoryGirl.create(:user)
#other_user = FactoryGirl.create(:user)
#yum = FactoryGirl.create(:yum, user: #other_user)
end
describe "liking a Yum" do
it "should increase the Yum's Like count" do
expect do
post "/v1/yums/#{#yum.id}/likes", { authorization: token_header(#user.auth_token) }
end.to change(#yum.reload.likes, :count).by(1)
end
end
describe "unliking a Yum" do
it "should increase the Yum's Like count" do
expect do
delete "/v1/yums/#{#yum.id}/likes", { authorization: token_header(#user.auth_token) }
end.to change(#yum.reload.likes, :count).by(-1)
end
end
end
I want the Likes controller to not require an ID for the destroy action since I use methods on my user model to like and unlike, but this scheme doesn't seem to be working, what am I doing wrong?
Try to add a route in your routes.rb like :
match "v1/yums/:yum_id/likes", to: "likes#destroy", via: "delete", defaults: { id: nil }

Resources