FactoryGirl issues with associations in Rspec/Rails - ruby-on-rails

Here is the method I am testing:
class User < ActiveRecord::Base
has_many :sports, :through => :user_sports, order: "user_sports.created_at", class_name: "Sport"
has_many :user_sports
def primary_sport
return nil if user_sports.blank?
user_sports.primary_only.first.sport
end
end
User Factory;
FactoryGirl.define do
sequence(:email) do |n|
"user#{n}#example.com"
end
factory :user do
email
first_name Faker::Name.first_name
last_name Faker::Name.last_name
password "password"
password_confirmation "password"
agreed_to_age_requirements true
username "testing123"
state "AL"
city_id 201
school_id 20935
handedness "Left"
customer_id { "#{rand(1000)}" }
sports {[create(:sport)]}
after(:create) do |user, elevator|
user.subscriptions << create(:subscription)
user.roles << create(:role)
end
end
factory :athlete, class: "Athlete", parent: :user do
type "Athlete"
recruit_year "2016"
end
end
Here is my test:
require 'spec_helper'
describe User do
describe "associations" do
it { should have_and_belong_to_many(:roles) }
it { should belong_to(:account_type) }
it { should belong_to(:primary_sport).class_name("Sport") }
it { should belong_to(:school) }
it { should belong_to(:city) }
it { should belong_to(:hometown) }
it { should have_many(:social_actions) }
it { should have_one(:invitation) }
it { should have_many(:authorizations) }
it { should belong_to(:user_type) }
it { should have_and_belong_to_many(:positions).class_name "SportPosition" }
it { should have_many(:sports).through(:user_sports) }
it { should have_many(:user_sports) }
it { should have_many :contributorships }
it { should have_many(:managed_athletes).through(:contributorships) }
it { should have_and_belong_to_many(:subscriptions) }
end
describe "nested attributes" do
it { should accept_nested_attributes_for(:user_sports) }
it { should accept_nested_attributes_for(:subscriptions) }
end
describe "validations" do
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should allow_value("test#test.com").for(:email) }
it { should_not allow_value("test.com").for(:email) }
end
describe "instance methods" do
before :each do
#user = create(:user, sports: [])
#school_admin_role = create(:role, name: "School Admin")
#contributor_role = create(:role, name: "Contributor")
end
describe "#my_athletes_path" do
it "returns a school admin path if the user has the role of School Admin" do
#user.roles << #school_admin_role
#user.my_athletes_path.should eq school_admin_athletes_path
end
it "returns a school admin path if the user has the role of Contributor" do
#user.roles << #contributor_role
#user.my_athletes_path.should eq contributor_dashboard_path
end
it "returns nil if the user has no Contributor or School Admin role" do
#user.my_athletes_path.should be_nil
end
end
describe "#first_time_login?" do
it "will evalute true if the user has logged in only once" do
#user.sign_in_count = 1
#user.save
#user.first_time_login?.should be_true
end
end
describe "#confirmation_required?" do
it "returns false" do
#user.confirmation_required?.should be_false
end
end
describe "#primary_sport", focus: true do
context "when user has no primary sport" do
it "returns nil" do
#user.primary_sport.should be_nil
end
end
context "when user has a primary sport" do
it "returns sport object" do
#user.sports << create(:sport)
#user.primary_sport.should eq #user.sports.first
end
end
end
end
end
This is the error I am receiving:
Failure/Error: #user.primary_sport.should eq #user.sports.first
NoMethodError:
undefined method sport for nil:NilClass
This is because when the user_sport association is created in the User Factory, the primary column is being set to false. Not sure how to fix this. Any help is greatly appreciated! Also, sorry for the ignorance on the TDD front, Im a newb

Couldn't you just add the following to your after(:create) block in the User factory:
us = user.user_sports.first
us.primary = true
us.save
That would ensure the association gets the primary flag.

Related

How to test this method? (RSpec)

def is_doctor
user = User.find_by(role: 'doctor')
"Name: #{user.name} | Email: #{user.email}| isActive: #{user.is_active}"
end
Something like this, but I don't know how to implement it correctly ↓
context 'test' do
#it { expect(user.is_doctor).to eq("Taras") }
end
I assume doctor? is an instance method on a User model + you're using Faker which might help you a lot.
# models/user.rb
class User < ApplicationRecord
def doctor?
return 'Not a doc' unless role == 'doctor'
"Name: #{name} | Email: #{email}| isActive: #{is_active}"
end
end
# specs/models/user_spec.rb
describe User, type: :model do
context 'with instance method' do
describe '#doctor?' do
subject { user. doctor? }
context 'with a doctor' do
let(:user) { create(:user, role: 'doctor') }
it 'includes name' do
expect(subject).to include("Name: #{user.name}")
end
it 'includes email' do
expect(subject).to include("Email: #{email}")
end
it 'includes is_active' do
expect(subject).to include("isActive: #{is_active}")
end
end
context 'without doctor' do
let(:user) { create(:user, role: 'foo') }
it 'has static response' do
expect(subject).to eq('Not a doc')
end
end
end
end
end

How do I test the comments of the current_user in RSpec?

So I have a User that has_many :comments.
In my Comments#Index, I have this:
def index
#comments = current_user.comments
end
Inside my Rspec.config... block in rails_helper.rb I have this:
# Add Config info for Devise
config.include Devise::TestHelpers, type: :controller
My comments_controller_spec.rb looks like this:
describe 'GET #index' do
it "populates an array of comments that belong to a user" do
user = create(:user)
node = create(:node)
comment1 = create(:comment, node: node, user: user)
comment2 = create(:comment, node: node, user: user)
get :index, { node_id: node }
expect(assigns(:comments)).to match_array([comment1, comment2])
end
it "renders the :index template"
end
This is my Users.rb factory:
FactoryGirl.define do
factory :user do
association :family_tree
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
password "password123"
password_confirmation "password123"
bio { Faker::Lorem.paragraph }
invitation_relation { Faker::Lorem.word }
# required if the Devise Confirmable module is used
confirmed_at Time.now
gender 1
end
end
This is my Comments factory:
FactoryGirl.define do
factory :comment do
association :node
message { Faker::Lorem.sentence }
factory :invalid_comment do
message nil
end
end
end
This is the error I am getting now:
Failure/Error: get :index, { node_id: node }
NoMethodError:
undefined method `comments' for nil:NilClass
Thoughts?
You need to sign in first:
describe 'GET #index' do
let(:user) { create(:user) }
let(:node) { create(:node) }
let(:comment1) { create(:comment, node: node, user: user) }
let(:comment2) { create(:comment, node: node, user: user) }
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
it "populates an array of comments that belong to a user" do
get :index, { node_id: node }
expect(assigns(:comments)).to match_array [comment1, comment2]
end
end
You could also create a module in your spec/support directory with the following code:
module SpecAuthentication
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = FactoryGirl.create :user
sign_in #user
end
end
and include it in your RSpec.configure block:
config.include SpecAuthentication
Now you can call the login_user method in your specs:
describe 'GET #index' do
let(:node) { create(:node) }
let(:comment1) { create(:comment, node: node, user: #user) }
let(:comment2) { create(:comment, node: node, user: #user) }
before { login_user }
it "populates an array of comments that belong to a user" do
get :index, { node_id: node }
expect(assigns(:comments)).to match_array [comment1, comment2]
end
end
Update
Instead of including the module in the configure block in your spec/rails_helper.rb file, you could also add a configure block in the support file (spec/support/devise.rb) itself:
module SpecAuthorization
...
end
RSpec.configure do |config|
config.include SpecAuthorization
end

2 Rspec tests fail due to nil:NillCLass error in model spec when others pass

I have the following Rspec test for a vote model, which includes a custom validation ensuring you can't vote on your own content, which is shown below. I am puzzled as to why only 2 of these tests fail with a nilclass error when the other tests within the spec pass.
#vote must be nil but why aren't the other tests failing with the same error?
vote.rb
validates :ensure_not_author
def ensure_not_author
votable = self.votable_type.downcase
errors.add(:user_id, "You can't vote on your own content.") if self.votable.user_id == self.user_id
end
factories
factory :answer do
user_id :user
question_id :question
body "you need to change your grip"
votes_count 0
correct false
end
factory :vote do
user_id :user
votable_id :answer
votable_type "Answer"
value 1
points 5
end
factory :user do |u|
u.sequence(:email) {|n| "test#{n}#hotmail.com"}
u.sequence(:username) {|n| "tester#{n}" }
u.password "password"
u.password_confirmation "password"
u.remember_me true
u.reputation 200
end
vote_spec.rb
require "spec_helper"
describe Vote do
before(:each) do
#user2 = FactoryGirl.create(:user)
#user = FactoryGirl.create(:user)
#answer = FactoryGirl.create(:answer, user_id: #user)
#vote = Vote.create(user_id: #user2.id, value: 1, points: 5, votable_id: #answer.id, votable_type: "Answer")
end
subject { #vote }
it { should respond_to(:user_id) }
it { should respond_to(:votable_id) }
it { should respond_to(:votable_type) }
it { should respond_to(:value) }
it { should respond_to(:points) }
describe 'value' do
before { #vote.value = nil }
it { should_not be_valid }
end
describe "user_id" do
before { #vote.user_id = nil }
it { should_not be_valid }
end
describe "votable_id" do
before { #vote.votable_id = nil }
it { should_not be_valid }
end
describe "votable type" do
before { #vote.votable_type = nil }
it { should_not be_valid }
end
describe "vote value" do
before { #vote.value = 5 }
it { should_not be_valid }
end
end
Failures:
1) Vote votable_id
Failure/Error: it { should_not be_valid }
NoMethodError:
undefined method `user_id' for nil:NilClass
# ./app/models/vote.rb:17:in `ensure_not_author'
# ./spec/models/vote_spec.rb:25:in `block (3 levels) in <top (required)>'
2) Vote votable type
Failure/Error: it { should_not be_valid }
NoMethodError:
undefined method `downcase' for nil:NilClass
# ./app/models/vote.rb:16:in `ensure_not_author'
# ./spec/models/vote_spec.rb:35:in `block (3 levels) in <top (required)>'
You validator ensure_not_author depends Vote#votable_type and Vote#votable to function well. And when you test validity of #vote, this validator will be tested.
However, in your "votable_id" testcase, you set votable_id to be nil. Later when you test #vote's validity with should_not be_valid, the ensure_not_author is called and failed at self.votable.user_id because ActiveRecord will query for Votable with votable_id.
Similarly, your "votable type" test case failed at self.votable_type.downcase since you set votable_type to be nil.
You should check the availability of the attributes in your validator before you send messages to them. Or write other validators to check them before ensure_not_author.

Testing carrierwave keeps returning "Image can't be blank"-error

I've been implementing carrierwave, which works great in the browser. However, my tests keep returning this:
Error
1) Item
Failure/Error: it { should be_valid }
expected valid? to return true, got false
# ./spec/models/item_spec.rb:36:in `block (2 levels) in <top (required)>'
factories.rb
include ActionDispatch::TestProcess
FactoryGirl.define do
sequence(:email) { |n| "User#{n}#example.com"}
factory :user do
name "John doe"
email
password "foobar"
password_confirmation "foobar"
end
factory :list do
name "Lorem ipsum"
user
end
factory :item do
image { fixture_file_upload(Rails.root.join('spec', 'support', 'test_images', 'google.png'), 'image/png') }
title "Shirt"
link "www.example.com"
list
end
end
item_spec.rb
require 'spec_helper'
describe Item do
let(:user) { FactoryGirl.create(:user) }
let(:list) { FactoryGirl.create(:list) }
before do
#item = list.items.build(title: "Lorem ipsum")
#item.valid?
puts #item.errors.full_messages.join("\n")
end
subject { #item }
it { should respond_to(:title) }
it { should respond_to(:list_id) }
it { should respond_to(:list) }
it { should respond_to(:image) }
it { should respond_to(:remote_image_url) }
its(:list) { should == list }
it { should be_valid }
describe "when list_id not present" do
before { #item.list_id = nil }
it { should_not be_valid }
end
describe "when image not present" do
before { #item.image = "" }
it { should_not be_valid }
end
describe "with blank title" do
before { #item.title = " " }
it { should_not be_valid }
end
describe "with title that is too long" do
before { #item.title = "a" * 141 }
it { should_not be_valid }
end
end
item.rb
class Item < ActiveRecord::Base
attr_accessible :link, :list_id, :title, :image, :remote_image_url
belongs_to :list
mount_uploader :image, ImageUploader
validates :title, presence: true, length: { maximum: 140 }
validates :list_id, presence: true
validates_presence_of :image
end
I have a image in the spec/support/test_images folder called google.png.
I'm very new to rails, and any help is thus much appreciated!
it { should be_valid }
is failing because (as you might expect) the subject is not valid. You need to find out why it is invalid. Try something like this:
it "should be valid" do
subject.valid?
subject.errors.should be_empty
end
Now the example will fail, but the error message will be more descriptive.
Another way to approach this is to add pry to your project. Then add binding.pry where you'd like to open a console:
it "should be valid" do
subject.valid?
binding.pry
subject.errors.should be_empty
end
Now you can inspect your test objects to figure out how the validation failed.
I feel quite stupid. Forgot to attach an image, which obviously caused the validation to fail.
Had to change:
before do
#item = list.items.build(title: "Lorem ipsum")
#item.valid?
puts #item.errors.full_messages.join("\n")
end
To:
before do
#item = list.items.build(title: "Lorem ipsum")
#item.image = fixture_file_upload('/test_images/google.png', 'image/png')
end

spec validation failure

The following tests are all passing except the "it { should be_valid }" lines in 'describe "sent treatings" do' and 'describe "received treatings" do'
require 'spec_helper'
describe Treating do
let(:requestee) { FactoryGirl.create(:user) }
let(:requestor) { FactoryGirl.create(:user) }
before { #received_treating = requestee.received_treatings.build(intro: "Lorem ipsum") }
before { #sent_treating = requestor.sent_treatings.build(intro: "Lorem ipsum") }
describe "sent treatings" do
subject { #sent_treating }
it { should respond_to(:intro) }
it { should respond_to(:requestor_id) }
it { should respond_to(:requestor) }
its(:requestor) { should == requestor }
it { should be_valid }
end
describe "received treatings" do
subject { #received_treating }
it { should respond_to(:intro) }
it { should respond_to(:requestee_id) }
it { should respond_to(:requestee) }
its(:requestee) { should == requestee }
it { should be_valid }
end
describe "accessible attributes" do
it "should not allow access to requestor_id" do
expect do
Treating.new(requestor_id: requestor.id)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
it "should not allow access to requestee_id" do
expect do
Treating.new(requestee_id: requestee.id)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
describe "when requestor_id is not present" do
before { #sent_treating.requestor_id = nil }
it { should_not be_valid }
end
describe "when requestee_id is not present" do
before { #received_treating.requestee_id = nil }
it { should_not be_valid }
end
end
here is the error:
Failures:
1) Treating sent treatings
Failure/Error: it { should be_valid }
expected valid? to return true, got false
# ./spec/models/treating_spec.rb:19:in `block (3 levels) in <top (required)>'
2) Treating received treatings
Failure/Error: it { should be_valid }
expected valid? to return true, got false
# ./spec/models/treating_spec.rb:30:in `block (3 levels) in <top (required)>'
lastly, my user.rb model:
class Treating < ActiveRecord::Base
attr_accessible :intro, :proposed_date, :proposed_location
validates :requestor_id, presence: true
validates :requestee_id, presence: true
belongs_to :requestor, class_name: "User"
belongs_to :requestee, class_name: "User"
end
any help is appreciated!
In this test you have two meetings and set only one of the two ids of each of them in the fixture:
before { #received_treating = requestee.received_treatings.build(intro: "Lorem ipsum") }
before { #sent_treating = requestor.sent_treatings.build(intro: "Lorem ipsum") }
assuming user is the same as in your last question,
class User < ActiveRecord::Base
...
has_many :sent_meetings, :foreign_key => "requestor_id", :class_name => "Meeting"
has_many :received_meetings, :foreign_key => "requestee_id", :class_name => "Meeting"
#received_treating should have an requestee_id but not an requester_id (has never been assigned anywhere!) and #sent_treating has a requestor_id.
so again, for the same reasons as in your last question, the validation fails, as both have only one of the two ids requested in the validation set.
What is the behaviour that you expect? If you want to build up the n:m relationship to the users, you will have to specify the second user at some point. Maybe you mean a fixture like that:
before do
#treating = requestor.sent_treatings.build(intro: "Lorem ipsum")
#treating.requestee = requestee
end
maybe you even want to create a custom setter in Treating
def send_to(user)
requestee = user
end
then you could write something like
before { #sent_treating = requestor.sent_treatings.build(intro: "Lorem ipsum").send_to(requestee) }
That gives you a Treating with both ids set.
Use before(:each) block
before(:each) {
#received_treating = requestee.received_treatings.build(intro: "Lorem ipsum")
#sent_treating = requestor.sent_treatings.build(intro: "Lorem ipsum")
}

Resources