spec validation failure - ruby-on-rails

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")
}

Related

Chapter 11 Michael Hartl RoR Rspec at end of 11.1.5 Fails (6 Errors)

I'm not sure what I did wrong as I am completely new to RoR. The errors focus around local variables or methods not being defined. I'm quite lost as to how most of the methods and such from files relate to other and can be used in others and so I'm not really sure how where to look to fix the undefined methods/ local variables. If any other files are needed then I can provide it upon request.
Failures:
1) when follower id is not present
Failure/Error: before { relationship.follower_id = nil }
NameError:
undefined local variable or method `relationship' for #<RSpec::Core::ExampleGroup::Nested_5:0x000000064cf4b8>
# ./spec/models/relationship_spec.rb:27:in `block (2 levels) in <top (required)>'
2) when followed id is not present
Failure/Error: before { relationship.followed_id = nil }
NameError:
undefined local variable or method `relationship' for #<RSpec::Core::ExampleGroup::Nested_4:0x000000062511c8>
# ./spec/models/relationship_spec.rb:22:in `block (2 levels) in <top (required)>'
3) follower methods
Failure/Error: it { should respond_to(:followed) }
expected "follower methods" to respond to :followed
# ./spec/models/relationship_spec.rb:16:in `block (2 levels) in <top (required)>'
4) follower methods
Failure/Error: it { should respond_to(:follower) }
expected "follower methods" to respond to :follower
# ./spec/models/relationship_spec.rb:15:in `block (2 levels) in <top (required)>'
5) follower methods followed
Failure/Error: its(:followed) { should eq followed }
NameError:
undefined local variable or method `followed' for #<RSpec::Core::ExampleGroup::Nested_3::Nested_2:0x00000006276158>
# ./spec/models/relationship_spec.rb:18:in `block (2 levels) in <top (required)>'
6) follower methods follower
Failure/Error: its(:follower) { should eq follower }
NameError:
undefined local variable or method `follower' for #<RSpec::Core::ExampleGroup::Nested_3::Nested_1:0x00000006280ea0>
# ./spec/models/relationship_spec.rb:17:in `block (2 levels) in <top (required)>'
relationship_spec.rb
require 'spec_helper'
describe Relationship do
let(:follower) { FactoryGirl.create(:user) }
let(:followed) { FactoryGirl.create(:user) }
let(:relationship) { follower.relationships.build(followed_id: followed.id) }
subject { relationship }
it { should be_valid }
end
describe "follower methods" do
it { should respond_to(:follower) }
it { should respond_to(:followed) }
its(:follower) { should eq follower }
its(:followed) { should eq followed }
end
describe "when followed id is not present" do
before { relationship.followed_id = nil }
it { should_not be_valid }
end
describe "when follower id is not present" do
before { relationship.follower_id = nil }
it { should_not be_valid }
end
user.spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
it { should respond_to(:remember_token) }
it { should respond_to(:authenticate) }
it { should respond_to(:admin) }
it { should respond_to(:microposts) }
it { should respond_to(:feed) }
it { should respond_to(:relationships) }
it { should respond_to(:followed_users) }
it { should respond_to(:reverse_relationships) }
it { should respond_to(:followers) }
it { should respond_to(:following?) }
it { should respond_to(:follow!) }
it { should respond_to(:unfollow!) }
it { should be_valid }
it { should_not be_admin }
.
.
.
describe "following" do
let(:other_user) { FactoryGirl.create(:user) }
before do
#user.save
#user.follow!(other_user)
end
it { should be_following(other_user) }
its(:followed_users) { should include(other_user) }
describe "followed user" do
subject { other_user }
its(:followers) { should include(#user) }
end
describe "and unfollowing" do
before { #user.unfollow!(other_user) }
it { should_not be_following(other_user) }
its(:followed_users) { should_not include(other_user) }
end
end
end
user.rb
class User < ActiveRecord::Base
has_secure_password
has_many :microposts, dependent: :destroy
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
has_many :followed_users, through: :relationships, source: :followed
before_save { email.downcase! }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
def feed
# This is preliminary. See "Following users" for the full implementation.
Micropost.where("user_id = ?", id)
end
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy
end
private
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
In relationship_spec.rb, you have a misplaced end which is causing the example group for Relationship getting closed beforehand so the rest of the example groups which should have been nested under it have no access of the local variables follower, followed and relationship and obviously no access to the subject that you intended to use in your examples i.e., relationship. This is causing the errors.
require 'spec_helper'
describe Relationship do
let(:follower) { FactoryGirl.create(:user) }
let(:followed) { FactoryGirl.create(:user) }
let(:relationship) { follower.relationships.build(followed_id: followed.id) }
subject { relationship }
it { should be_valid }
## end ## <<-- REMOVED this end of "describe Relationship"
describe "follower methods" do
it { should respond_to(:follower) }
it { should respond_to(:followed) }
its(:follower) { should eq follower }
its(:followed) { should eq followed }
end
describe "when followed id is not present" do
before { relationship.followed_id = nil }
it { should_not be_valid }
end
describe "when follower id is not present" do
before { relationship.follower_id = nil }
it { should_not be_valid }
end
end ## <-- ADDED end of "describe Relationship"
NOTE: Proper indentation can really help you to detect this kind of issues in future.

FactoryGirl issues with associations in Rspec/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.

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.

Writing Tests for Model Name Uniqueness

I'm have a tutorial model which belongs to a user a model. I would like the tutorial titles to be unique on per user level. So two users can have tutorials with the same titles but a user can't have two tutorials with the same title. My test is failing but I know that I'm correcting filtering out titles that are repeated. What is wrong with my test?
# model - tutorial.rb
class Tutorial < ActiveRecord::Base
attr_accessible :title
belongs_to :user
validates :user_id, presence: true
validates :title, presence: true, length: { maximum: 140 }, uniqueness: { :scope => :user_id }
end
# spec for model
require 'spec_helper'
describe Tutorial do
let(:user) { FactoryGirl.create(:user) }
before do
#tutorial = FactoryGirl.create(:tutorial, user: user)
end
subject { #tutorial }
describe "when a title is repeated" do
before do
tutorial_with_same_title = #tutorial.dup
tutorial_with_same_title.save
end
it { should_not be_valid }
end
end
# rspec output
Failures:
1) Tutorial when a title is repeated
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/tutorial_spec.rb:50:in `block (3 levels) in <top (required)>'
The problem with the test is this line:
it { should_not be_valid }
This spec checks valid? on the subject of your test, which is #tutorial - which is valid.
Suggested Refactoring:
describe Tutorial do
let(:user) { FactoryGirl.create(:user) }
before do
#tutorial = FactoryGirl.create(:tutorial, user: user)
end
subject { #tutorial }
describe "when a title is repeated" do
subject { #tutorial.dup }
it { should_not be_valid }
end
end

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

Resources