Test failing when trying to run Rspec with Factory Girl - ruby-on-rails

While trying to incorporate Factory Girl in my project I'm running into an error I just can't seem to solve. I wrote a test that would check if my user's first name was empty:
# spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, :type => :model do
it 'is invalid without a first name' do
user = FactoryGirl.build(:user, first_name: nil)
expect(user).to have(1).errors_on(:first_name)
end
end
Unfortnately when I try to run this test I get this error:
1) User is invalid without a first name
Failure/Error: expect(user).to have(1).errors_on(:first_name)
expected 1 errors on :first_name, got 2
# ./spec/models/user_spec.rb:7:in `block (2 levels) in '
Here's what my factories.rb file looks like:
# spec/factories.rb
FactoryGirl.define do
factory :user do
first_name "John"
last_name "Doe"
sequence(:email) {|n| "johndoe#{n}#example.com"}
password "secret"
end
end
If it helps at all here's how my gemfile is setup:
group :development, :test do
gem 'rspec-rails'
gem 'rspec-collection_matchers'
gem 'factory_girl_rails'
end
Update
After checking my User model I believe that the second error was me mistakenly setting the presence validation twice in my model:
validates :first_name, :last_name, :email, :password, presence: true
validates :first_name, :last_name, presence: true, format: {with: /\A([^\d\W]|[-])*\Z/, message: 'cannot have any numbers or special characters'}
What I wonder now is a way for rspec to somehow point out the errors I'm dealing with instead of vaguely telling me:
expected 1 errors on :first_name, got 2

It seems that your user actually have 2 errors on the first_name field
To debug it you can just print the errors
RSpec.describe User, :type => :model do
it 'is invalid without a first name' do
user = FactoryGirl.build(:user, first_name: nil)
puts user.errors.messages[:first_name]
expect(user).to have(1).errors_on(:first_name)
end
end

Related

rails 5 validators not protecting against a missing attribute during Model create?

cannot seem to get my validators to work to ensure all attributes are present to allow a User to be created. Basic User with 2 attributes
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
tests to check that name and email are present when created. these #pass
RSpec.describe User, type: :model do
context 'validations' do
subject { FactoryGirl.build(:user) }
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_presence_of(:name) }
it "fails to create user unless both are present" do
expect { User.create(:name => 'jo bloggs1', :noemail => 'c#c.co')}.to raise_error(ActiveModel::UnknownAttributeError)
end
end
end
but if i try and create model with a missing attribute no error is raised
it "fails to create user unless both are present" do
expect { User.create(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
end
result
1) User validations fails to create user unless both are present
Failure/Error: expect { User.create(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
expected ActiveModel::MissingAttributeError but nothing was raised
# ./spec/models/user_spec.rb:12:in `block (3 levels) in <top (required)>'
fyi, FactoryGirl
FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
end
end
i have tried clever stuff like
class User < ApplicationRecord
# before_create :run_it
after_initialize :all_present?
validates :name, presence: true
validates :email, presence: true
private
def all_present?
if (#email.nil? || #name.nil?)
raise ActiveModel::MissingAttributeError.new()
end
end
end
but cannot seem to raise these manually...?
what am i doing wrong?
tx all
Ben
The problem is that there are 2 methods, create and create!. The first, create
The resulting object is returned whether the object was saved successfully to the database or not
Whereas with create!:
Raises a RecordInvalid error if validations fail, unlike Base#create
So, create fails silently and doesn't raise any exceptions, but you can still inspect the instance and see that it's a new record and has errors and such, and create! fails noisily, by raising the error you are expecting it to raise. In short, your test should be:
it "fails to create user unless both are present" do
expect { User.create!(:name => 'jo bloggs1')}.to raise_error(ActiveModel::MissingAttributeError)
end

Why does this spec of my model's uniqueness validation fail when it should pass?

I am learning testing with RSpec. Something is not working with my tests.
My model:
class User < ActiveRecord::Base
has_secure_password
# Validation macros
validates_presence_of :name, :email
validates_uniqueness_of :email, case_sensitive: false
end
My factory:
FactoryGirl.define do
factory :user do
name "Joe Doe"
email "joe#example.com"
password_digest "super_secret_password"
end
end
And my spec:
require 'rails_helper'
RSpec.describe User, type: :model do
user = FactoryGirl.build(:user)
it 'has a valid factory' do
expect(FactoryGirl.build(:user)).to be_valid
end
it { is_expected.to respond_to(:name) }
it { is_expected.to respond_to(:email) }
it { is_expected.to respond_to(:password) }
it { is_expected.to respond_to(:password_confirmation) }
it { expect(user).to validate_presence_of(:name) }
it { expect(user).to validate_presence_of(:email) }
it { expect(user).to validate_presence_of(:password) }
it { expect(user).to validate_uniqueness_of(:email).case_insensitive }
end
I expected this test to pass. But I get this as a result:
Failures:
1) User should validate that :email is case-insensitively unique
Failure/Error: it { expect(user).to validate_uniqueness_of(:email).case_insensitive }
User did not properly validate that :email is case-insensitively unique.
The record you provided could not be created, as it failed with the
following validation errors:
* name: ["can't be blank"]
# ./spec/models/user_spec.rb:18:in `block (2 levels) in <top (required)>'
Finished in 0.34066 seconds (files took 1.56 seconds to load) 9
examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:18 # User should validate that :email
is case-insensitively unique
What I am missing?
Update
I think that this is a bug: https://github.com/thoughtbot/shoulda-matchers/issues/830
It is because you are declaring it 2 times IMO! First building user then building same user inside expect().
Just use ur first user that you have built with factory-bot like so:
it 'has a valid factory' do
expect(user).to be_valid
end
P.S
It is better to use Faker gem instead of using harcoded instances like you did in factory.rb
Your Variable Is Currently Only Set Once for All Tests
When you write code like:
RSpec.describe User, type: :model do
user = FactoryGirl.build(:user)
end
you aren't building a new user each time you run a new spec. Likewise, using #let is the wrong approach, because it memoizes the variable even between tests. Instead, you need a to use an RSpec before#each block. For example:
describe User do
before do
#user = FactoryGirl.build :user
end
# some specs
end
If you have tests which are persisting you user to the database, and if you have disabled rollback or database cleaning between tests, then your defined factory (as currently written) will certainly fail the uniqueness validation. In such cases, you may want to try:
User.delete_all in your test, or otherwise cleaning your database between tests.
Using FactoryGirl sequences or the Faker gem to ensure that user attributes are actually unique.
USE let
RSpec.describe User, type: :model do
let(:user) { FactoryGirl.build(:user) }
# other what you need

RSpec NoMethodError when calling should_not_be_valid on FactoryGirl.build

In my RSpec user_spec.rb one of my FactoryGirl factories seems valid and I can call should be_valid on it, and this test will pass (and will break if I put an empty string in the factory user_name).
describe User do
it "has a valid factory" do
# should be_valid works fine
FactoryGirl.create(:user).should be_valid
end
it "is invalid without a name" do
# should_not_be_valid throws a NoMethodError
FactoryGirl.build(:user, :user_name => nil).should_not_be_valid
end
end
However when I call the above should_not_be_valid on FactoryGirl.build, the test fails:
1) User is invalid without a name
Failure/Error: FactoryGirl.build(:user, :user_name => nil).should_not_be_valid
NoMethodError:
undefined method `should_not_be_valid' for #<User id: nil, user_name: nil, created_at: nil, updated_at: nil>
# ./spec/models/use
When it should instead pass, since a blank user_name is invalid. Here is what I have in my factories.rb:
FactoryGirl.define do
factory :user do
user_name "Foo Bar"
end
end
And my User model:
class User < ActiveRecord::Base
has_one :musician
validates :user_name, presence: true
end
How could should be_valid be working, but should_not_be_valid throws a NoMethodError?
I would really appreciate any help! I am running Rails 4, gem "rspec-rails", "~> 2.14.1", and gem 'factory_girl_rails', '4.3.0'
Been trying to puzzle this one out for awhile with no success...
Use space between should_not and be_valid:
FactoryGirl.build(:user, :user_name => nil).should_not be_valid

Two similar rspec tests, giving opposite results

I have a user model:
class User < ActiveRecord::Base
attr_accessible :email, :name
# admin only
attr_accessible :email, :name, :admin, :as => :admin
And the following model spec:
describe "accessible attributes" do
let(:new_user){ FactoryGirl.create(:user) }
#admin_attrs = { admin: true, name: "ben", email: "xyz#test.com"}
it "can not be set on create" do
# variant 1 - test fails, AM::MAS::Error NOT thrown
expect do
User.new(#admin_attrs)
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
# variant 2 - test passes, AM::MAS::Error NOT thrown
expect do
User.new(admin: true, name: "ben", email: "xyz#test.com", password: "123xyz", password_confirmation: "123xyz")
end.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
I can't figure out why variant 1 of my test fails, but variants 2 passes. The error message I get is:
1) User accessible attributes can not be set on create
Failure/Error: expect do
expected ActiveModel::MassAssignmentSecurity::Error but nothing was raised
They are basically the same test. What am I doing wrong? In fact, if I perform test 1 from the console, it does throw a MAS::Error, as expected. I'm confused.
#admin_attrs is probably nil as far as your first text example is concerned because it's not being set up in a before hook (or through 'let')

rspec testing model validations with subject - error

This is my test for validation, i would like to find the best way to writing model specs, especially for validation. But I have problem with this code below.
require 'spec_helper'
describe Ad, :focus do
let(:ad) { Ad.sham!(:build) }
specify { ad.should be_valid }
it "not creates a new instane given a invalid attribute" do
ad = Ad.new
ad.should_not be_valid
end
[:title, :category_id, :email, :ad_content, :name, :price].each do |attr|
it "should require a #{attr}" do
subject.errors[attr].should include("blank")
end
end
end
When I run this spec i receive this error:
5) Ad should require a name
Failure/Error: subject.errors[attr].should include("blank")
expected [] to include "blank"
Diff:
## -1,2 +1,2 ##
-blank
+[]
# ./spec/model/ad_spec.rb:15:in `block (3 levels) in <top (required)>'
The problem here is that you're not calling valid? in that example before checking for errors. You're calling it (indirectly) in the previous example, but not the one that you're asserting that has errors.
The correct way is this:
[:title, :category_id, :email, :ad_content, :name, :price].each do |attr|
it "should require a #{attr}" do
subject.valid?
subject.errors[attr].should include("blank")
end
end

Resources