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?
Related
I'm trying to create a model method that counts the number of posts for a user, and then test it with Rspec.
But I'm running into an error,
undefined method `count_posts' for #<User:0x000000044d42a8>
User Model
has_many :posts
def self.count_posts
self.posts.all.count
end
Posts Model
belongs_to :user
User_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe "count_posts" do
before do
#user1 = create(:user)
post = create(:post, user: #user1)
end
it "Returns number of posts for a user" do
expect( #user1.count_posts ).to eq(1)
end
end
end
/factories/users.rb
FactoryGirl.define do
factory :user do
sequence(:email, 100) { |n| "person#{n}#example.com"}
password "helloworld"
password_confirmation "helloworld"
end
end
/factories/posts.rb
FactoryGirl.define do
factory :post do
title "Post Title"
body "Post bodies must be pretty long."
user
end
end
I don't understand why its an undefined method, unless I've written it incorrectly in the model (which I fully accept as possible).
Apologies in advance if this question is too newbish. But I haven't fully grasped Rspec testing or the use of self.
According to your logic, count_posts must be an instance method instead of class method:
class User < ActiveRecord::Base
has_many :posts
def count_posts
posts.count # or posts.size
end
end
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
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]
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
I spent most of the day trying to root out a problem with a controller spec, and the current workaround seems unacceptable to me. Any take on why this works? ... and what I should do instead.
Given a simple hierarchy as follows, and the following ability.rb, the properties_controller_spec.rb does not allow the spec below to pass without the line saying:
ability = Ability.new(subject.current_user)
Can you tell me why this would be?
Thanks!
Models:
class Account < ActiveRecord::Base
has_many :properties, :dependent => :nullify
end
class Property < ActiveRecord::Base
belongs_to :account
end
class User < Refinery::Core::BaseModel #for RefineryCMS integration
belongs_to :account
end
Ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? :user
can [:read, :create, :update, :destroy], Property, account_id: user.account_id
else
can [:show], Property
end
end
end
properties_contoller_spec.rb:
require 'spec_helper'
describe PropertiesController do
def valid_attributes
describe "Authenticated as Property user" do
describe "PUT update" do
describe "with invalid params" do
it "re-renders the 'edit' template" do
property = FactoryGirl.create(:property, account: property_user.account)
# Trigger the behavior that occurs when invalid params are submitted
Property.any_instance.stub(:save).and_return(false)
ability = Ability.new(subject.current_user) # seriously?
put :update, {:id => property.to_param, :property => { }}, {}
response.should render_template("edit")
end
end
end
end
end
Arg! Found it myself.
Here it is:
config.include Devise::TestHelpers, :type => :controller
Following is the code to sign in the property_user, as directed by the Devise docs. (The locals in question are created in a global_variables.rb that is included. These are used all over the place.)
def signed_in_as_a_property_user
property_user.add_role "User"
sign_in property_user
end
def sign_in_as_a_property_user
property_user.add_role 'User'
post_via_redirect user_session_path,
'user[email]' => property_user.email,
'user[password]' => property_user.password
end