Error RSpec ActionController::UrlGenerationError with Rails - ruby-on-rails

I'm getting an error when trying to test a Rails Controller with RSpec. It's a double nested route and I'm trying to figure out the right syntax but hadn't had many luck yet.
The error that I'm getting is
Failure/Error: get :index, {category_id: category.to_param, id: system.to_param}
ActionController::UrlGenerationError:
No route matches {:action=>"index", :category_id=>"220", :controller=>"reviews", :id=>"166"}
# ./spec/controllers/reviews_controller_spec.rb:11:in `block (3 levels) in <top (required)>'
I've made the same test for the system controller which works fine. The webpage works fine as well. No errors with that (just this error with testing).
Here is what the RSpec test look like:
require 'rails_helper'
RSpec.describe ReviewsController, type: :controller do
let (:category) { create(:category) }
let (:system) { create(:system) }
let (:reviews) { create_list(:review, 3, category: category, system: system) }
describe "GET index" do
it "assigs all reviews to an instance var called #reviews" do
get :index, {category_id: category.to_param, id: system.to_param}
expect(assigns(:reviews)).to eq reviews
end
it "assigns all the reviews to an var called #system" do
get :index, system_id: system.to_param
expect(assigns(:system)).to eq system
end
end
describe "system scope" do
before { create(:review) }
it "only assigns reviews index in the current system" do
get :index, {category_id: category.to_param, id: system.to_param}
expect(assigns(:reviews)).to eq reviews
end
end
end
This is the Controller that it's testing:
class ReviewsController < ApplicationController
def index
#system = System.find(params[:system_id])
#reviews = #system.reviews
respond_to do |format|
format.html
format.json { render json: { system: #system, reviews: #reviews } }
end
end
def show
#system = System.find(params[:system_id])
#review = #system.reviews
end
end
And these are the routes:
Rails.application.routes.draw do
root "categories#index"
resources :categories do
resources :systems do
resources :reviews
end
end
end
Here are the models:
Category Model
class Category < ActiveRecord::Base
validates_presence_of :name
has_many :systems
end
System Model
class System < ActiveRecord::Base
belongs_to :category
has_many :reviews
validates_presence_of :name, :category
end
Review Model
class Review < ActiveRecord::Base
belongs_to :system
validates_presence_of :content, :system
end

ActionController::UrlGenerationError: No route matches
{:action=>"index", :category_id=>"220", :controller=>"reviews",
:id=>"166"}
According to your routes, that particular index route expects category_id and system_id as keys. You need to change :id to :system_id. The below should work.
it "only assigns reviews index in the current system" do
get :index, {category_id: category.to_param, system_id: system.to_param}
expect(assigns(:reviews)).to eq reviews
end
Update
NoMethodError: undefined method `category=' for
Review:0x005578a9e87188
There is no association between a review and a category. Edit your models to set the association accordingly.
#category.rb
class Category < ActiveRecord::Base
validates_presence_of :name
has_many :systems
has_many :reviews
end
#review.rb
class Review < ActiveRecord::Base
belongs_to :system
belongs_to :category
validates_presence_of :content, :system
end

Related

Rspec undefined method build for #<Array:xxxx>

I am getting this error message from Rspec
Failure/Error: #address = #owner.addresses.build
NoMethodError:
undefined method `build' for #<Array:0x007f9faba657f0>
Below is snippet from my controller
class AddressesController < ApplicationController
def new
#owner = Owner.find(params[:owner_id])
#address = #owner.addresses.build
end
end
Below is snippet from my spec file:
describe "GET #new" do
let(:owner) { create(:owner) }
before { xhr :get, :new, owner_id: owner.id }
it 'response will be success' do
expect(response).to be_success
end
end
Below is my route for addresses#new
new_owner_address GET /owners/:owner_id/addresses/new(.:format) addresses#new
addresses.build method is working fine in console and working fine in my application but it is failing on spec. Any suggestion will be highly appreciated.
Update:
factory:
FactoryGirl.define do
factory :owner do
name 'foo'
address 'bar'
amount 200.00
country 'foobar'
state 'qax'
end
end
Model and Associations:
class Owner < ActiveRecord::Base
has_many :addresses, dependent: :destroy
end
class Address < ActiveRecord::Base
belongs_to :owner
end
#owner.addresses has to be an ActiveRecord::Relation in order to respond to #build. However, it seems to return an Array in your spec. Does your factory override the default association somehow?

RSpec testing with followerships / friendships has many through users

I inspired myself with the following link, http://railscasts.com/episodes/163-self-referential-association, but the rspec testing is not coming easy.
user model:
class User < ActiveRecord::Base
# Associations
has_many :followerships
has_many :followers, :through => :followerships
has_many :inverse_followerships, :class_name => "Followership", :foreign_key => "follower_id"
has_many :inverse_followers, :through => :inverse_followerships, :source => :user
end
followership model:
class Followership < ActiveRecord::Base
belongs_to :user
belongs_to :follower, :class_name => "User"
end
followerhip factory:
FactoryGirl.define do
factory :followership do
user_id 1
follower_id 1
end
end
followerships controller:
class FollowershipsController < InheritedResources::Base
def create
#followership = current_user.followerships.build(:follower_id => params[:follower_id])
if #followership.save
flash[:notice] = "Following."
redirect_to root_url
else
flash[:error] = "Unable to follow."
redirect_to root_url
end
end
def destroy
#followership = current_user.followerships.find(params[:id])
#followership.destroy
flash[:notice] = "Removed followership."
redirect_to current_user
end
end
folowerships controller spec (this is all wrong):
require 'rails_helper'
describe FollowershipsController do
let!(:followership) { create(:followership) }
let!(:follower) { followership.follower }
let!(:user) { create(:user) }
before do
sign_in :user, user
end
describe "#create" do
it "saves the followership" do
post :create, followership: { follower_id: follower }
expect(response).to redirect_to(root_path)
expect(assigns(:followership).followership.followers).to eq(user)
expect(flash[:notice]).to eq("Following.")
end
it "fails to save followership" do
expect(post :create, followership: { follower_id: follower }).to redirect_to(root_path)
expect(flash[:notice]).to eq("Unable to follow.")
end
end
describe "#destroy" do
it "deletes the followership" do
expect {
delete :destroy, id: follower
}.to change(Followership, :count).by(-1)
expect(flash[:notice]).to eq("Removed followership.")
end
end
end
Error from followerships controller Rspec
FollowershipsController
#destroy
deletes the followership (FAILED - 1)
#create
saves the followership (FAILED - 2)
fails to save followership (FAILED - 3)
Failures:
1) FollowershipsController#destroy deletes the followership
Failure/Error: delete :destroy, id: follower
ActionController::UrlGenerationError:
No route matches {:action=>"destroy", :controller=>"followerships", :id=>nil}
2) FollowershipsController#create saves the followership
Failure/Error: expect(assigns(:followership).followership.followers).to eq(user)
NoMethodError:
undefined method `followership' for #<Followership:0x00000109f69780>
3) FollowershipsController#create fails to save followership
Failure/Error: expect(flash[:notice]).to eq("Unable to follow.")
expected: "Unable to follow."
got: "Following."
(compared using ==)
Thanks for the help :)
The let command uses lazy evaluation, so these records are not actually created until called. Use the let! syntax to ensure they're created before your tests run:
let!(:followership) { create(:followership) }
let!(:follower) { followership.follower }
let!(:user) { create(:user) }
Make sure your validations also only allow creation of a following if it doesn't already exist for that pair of users:
class Followership < ActiveRecord::Base
validates_uniqueness_of :user_id, scope: :follower_id
Also, it's not guaranteed that the follower/followership relationships will belong to user since user doesn't necessarily have an id of 1.
Finally, assigns is a method, so the syntax should be assigns(:followership) not assigns[:followership]

Testing Associations With RSpec in a Chess Application

I'm currently working with a small team on an open source Rails 4 chess application, and I'm trying to test out various possible piece moves in RSpec (including special cases such as en passant and castling). A senior web developer suggested that I use a separate table to keep track of the moves taken in each game of chess. After taking him up on his suggestion, I'm having trouble testing out valid moves, as shown in the error message below. I suspect that it might be a problem with my associations, but the teammates that I was able to talk to about this were unsure about the cause.
Any help would be greatly appreciated.
Error message:
Failures:
1) PiecesController Action: pieces#update should create a move when a move is valid
Failure/Error: #current_game ||= current_piece.game
NoMethodError:
undefined method `game' for nil:NilClass
# ./app/controllers/pieces_controller.rb:36:in `current_game'
# ./app/controllers/pieces_controller.rb:40:in `require_authorized_for_current_game'
# ./spec/controllers/pieces_controller_spec.rb:12:in `block (3 levels) in <top (required)>'
The test:
RSpec.describe PiecesController, type: :controller do
describe "Action: pieces#update" do
it "should create a move when a move is valid" do
user_sign_in
game = FactoryGirl.create(:game)
# Test a white pawn's movement on its first turn:
piece = FactoryGirl.create(:piece)
move = FactoryGirl.create(:move)
# Why can't I call game.id below?
patch :update, :id => game.id, :pieces => { }
piece_params = { :x_position => piece.x_position, :y_position => piece.y_position, :type => "Pawn" }
if piece.valid_move?(piece_params)
...
end
end
end
private
def user_sign_in
user = FactoryGirl.create(:user)
sign_in user
end
end
Associations:
class Game < ActiveRecord::Base
has_many :pieces
has_many :moves, through: :pieces
belongs_to :white_player, class_name: 'User', foreign_key: :white_player_id
belongs_to :black_player, class_name: 'User', foreign_key: :black_player_id
...
end
class Piece < ActiveRecord::Base
belongs_to :game
has_many :moves
def valid_move?(params)
...
end
...
end
class Move < ActiveRecord::Base
belongs_to :piece
end
Factories:
FactoryGirl.define do
factory :user do
...
end
factory :game do
association :white_player, factory: :user
association :black_player, factory: :user
turn 1
end
factory :piece do
association :game
...
end
# Set up an initially empty move, then adjust the values after checking that a piece can be moved:
factory :move do
association :piece
...
end
end
The controller:
class PiecesController < ApplicationController
before_action :authenticate_user!
before_action :require_authorized_for_current_game, only: [:update]
before_action :require_authorized_for_current_piece, only: [:update]
def update
...
end
...
private
def current_piece
#current_piece ||= Piece.find_by_id(params[:id])
end
...
def piece_params
params.require(:piece).permit(:x_position, :y_position, :type, :captured)
end
def current_game
#current_game ||= current_piece.game
end
def require_authorized_for_current_game
if current_game.white_player != current_user && current_game.black_player != current_user
render text: 'Unauthorized', status: :unauthorized
end
end
end

Tests for polymorphic nested resource

I'm trying to fix the default tests for a polymorphic nested resource and running into lots of issues. I'm betting there is a simple solution that this newbie just hasn't wrapped his head around yet. Any help would be greatly appreciated as always.
My Models:
class Member < ActiveRecord::Base
has_many :names, as: :person
has_many :dependents
attr_accessible :active, :deleted
end
class Name < ActiveRecord::Base
belongs_to :person, polymorphic: true
attr_accessible :dob, :dod, :first, :gender, :last, :mi, :prefix, :relation, :suffix
end
routes.rb:
resources :dependents do
resources :names
end
resources :members do
resources :names
end
Example Test:
require 'test_helper'
class NamesControllerTest < ActionController::TestCase
setup do
#name = names(:one)
#person = members(:one)
end
test "should get edit" do
get :edit, id: #name
assert_response :success
end
end
Error I'm receiving:
3) Error:
test_should_get_edit(NamesControllerTest):
ActionController::RoutingError: No route matches {:id=>"980190962", :person_id=>"980190962", :controller=>"names", :action=>"edit"}
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:544:in raise_routing_error'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:540:inrescue in generate'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:532:in generate'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:573:ingenerate'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:569:in generate_extras'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_dispatch/routing/route_set.rb:565:inextra_keys'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_controller/test_case.rb:153:in assign_parameters'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_controller/test_case.rb:465:inprocess'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_controller/test_case.rb:49:in process'
/usr/local/rvm/gems/ruby-1.9.3-p392/gems/actionpack-3.2.13/lib/action_controller/test_case.rb:392:inget'
/Users/mkenney/hraccess/test/functional/names_controller_test.rb:43:in `block in '
If I add back in the non-nested route to names these errors run. How do I tell the test that it is a nested resource and does this have anything to do with the polymorphic relationship, or is that just noise that is throwing me off?
Thanks in advance for any help you can offer this newbie!
Mark
I needed to add the member_id to get the proper route I believe:
test "should get edit" do
get :edit, member_id: #person, id: #name
assert_response :success
end

Testing an Rspec Controller with associations

I've got two models:
class Solution < ActiveRecord::Base
belongs_to :user
validates_attachment_presence :software
validates_presence_of :price, :language, :title
validates_uniqueness_of :software_file_name, :scope => :user_id
has_attached_file :software
end
class User < ActiveRecord::Base
acts_as_authentic
validates_presence_of :first_name, :last_name, :primary_phone_number
validates_uniqueness_of :primary_phone_number
has_many :solutions
end
with my routes looking like this:
map.resources :user, :has_many => :solutions
Now I'm trying to test my solutions controllers with the following RSpec test:
describe SolutionsController do
before(:each) do
#user = Factory.build(:user)
#solution = Factory.build(:solution, :user => #user)
end
describe "GET index" do
it "should find all of the solutions owned by a user" do
Solution.should_receive(:find_by_user_id).with(#user.id).and_return(#solutions)
get :index, :id => #user.id
end
end
end
However, this gets me the following error:
ActionController::RoutingError in 'SolutionsController GET index should find all of the solutions owned by a user'
No route matches {:id=>nil, :controller=>"solutions", :action=>"index"}
Can anybody point me to how I can test this, since the index should always be called within the scope of a particular user?
Factory#build builds an instance of the class, but doesn't save it, so it doesn't have an id yet.
So, #user.id is nil because #user has not been saved.
Because #user.id is nil, your route isn't activated.
try using Factory#create instead.
before(:each) do
#user = Factory.create(:user)
#solution = Factory.create(:solution, :user => #user)
end
Looks like your other problem is on this line:
get :index, :id => #user.id
You're trying to make a request to the index method, but you've provided the wrong variable name. When testing SolutionsController id implies a solution id, you need to supply the user id. This should work, or at least move you forward:
get :index, :user_id => #user.id

Resources