Two similar rspec tests, giving opposite results - ruby-on-rails

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')

Related

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

Test failing when trying to run Rspec with Factory Girl

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

rails 3 tutorial : rspec + factory_girl_rails problem

i've been following the Rails tutorial (http://railstutorial.org/chapters/beginning , Rails 3 version), and i've stopped at 11th chapter when using Factory Girl and Rspec, I have a test that isn't passing and I feel I'm doing something wrong but I don't see what.
First of all there is a git repository on Github with the code that doesn't pass that test.
http://github.com/Monomachus/ch3_static_pages
So I got users model
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
has_many :microposts
.
.
.
I got microposts model
class Micropost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
default_scope :order => 'microposts.created_at DESC'
end
Then I got Factory girl settings
Factory.define :user do |user|
user.name "Michael Hartl"
user.email "mhartl#example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.define :micropost do |micropost|
micropost.content "Foo bar"
micropost.association :user
end
And finally Rspec code
require 'spec_helper'
describe Micropost do
.
.
describe "microposts associations" do
before(:each) do
#user = User.create(#attr)
#mp1 = Factory(:micropost, :user => #user, :created_at => 1.day.ago)
#mp2 = Factory(:micropost, :user => #user, :created_at => 1.hour.ago)
end
it "should have a microposts attribute" do
#user.should respond_to(:microposts)
end
it "should be in the reverse order of appearing" do
#user.microposts.should == [#mp2, #mp1]
end
end
end
And I got the error which definitely tells me that I do something wrong.
Failures:
1) Micropost microposts associations should be in the reverse order of appearing
Failure/Error: #user.microposts.should == [#mp2, #mp1]
expected: [#<Micropost id: 2, content: "Foo bar", user_id: nil, created_at: "2010-12-24 12:47:02", update
d_at: "2010-12-24 13:47:02">, #<Micropost id: 1, content: "Foo bar", user_id: nil, created_at: "2010-12-23 13:
47:02", updated_at: "2010-12-24 13:47:02">],
got: [] (using ==)
Diff:
## -1,3 +1,2 ##
-[#<Micropost id: 2, content: "Foo bar", user_id: nil, created_at: "2010-12-24 12:47:02", updated_at: "20
10-12-24 13:47:02">,
- #<Micropost id: 1, content: "Foo bar", user_id: nil, created_at: "2010-12-23 13:47:02", updated_at: "20
10-12-24 13:47:02">]
+[]
# ./spec/models/micropost_spec.rb:42:in `block (3 levels) in <top (required)>'
As you can see even the user_id property is not set correctly +
apparently #user.microposts doesn't have any elements.
Please help me with this issue thanks.
Well the answer was simple :)
I included microposts associations in the Micropost spec.
And clearly
describe "microposts associations" do
before(:each) do
#user = User.create(#attr)
#mp1 = Factory(:micropost, :user => #user, :created_at => 1.day.ago)
#mp2 = Factory(:micropost, :user => #user, :created_at => 1.hour.ago)
end
it "should have a microposts attribute" do
#user.should respond_to(:microposts)
end
it "should be in the reverse order of appearing" do
#user.microposts.should == [#mp2, #mp1]
end
end
#attr did not contain the user properties but the micropost properties and of course #user = nil and then everything makes sense. So if you do have the same problem, include this code into User spec.
Now all my tests pass :)
By the time I had finished the pagination chapter, the tutorial was creating 100 sample users using Faker (listing 10.25 on page 390), and in RubyMine I was able to see my test was failing because the program was throwing an exception on duplicate user email address (which has a unique constraint). The #attr on line 8 of user_spec.rb has :email => "user#example.com", however this throws an exception since it's a duplicate email (I guess because Faker has already created it).
For me the fix was to copy #attr from line 8 and paste it into the describe "micropost associations" block (user_spec.rb), and change the email address to :email => "user#example999.com". I'm sure this is a total hack but I'm a n00b.
Update:
Another fix for me was to comment out the line #user = User.create(#attr), and simply create #mp1 and #mp2.
I was also getting test failure in this section, even though I already had "micropost associations" in user_spec.rb. Turns out I needed to restart spork and autotest in order to get them to use the new "micropost" factory in factories.rb.

Devise/Rspec - Tested a user creation with (maybe) missing attributes (got true..)

I am testing Devise with Rspec using Micheal Hartl source code (railstutorial)
Whereas the confirmable module is enabled, I don't understand why this test pass:
spec/models/user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
#attr = { :username => "ExampleUser",
:email => "user#example.com",
:password => 'test1234',
}
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
end
Basically, I want to be sure of this code does, it tests the creation on the user, not this validation (cause the user has not confirmed yet and the test returns true) ? This is right?
Moreover, I didn't provide attribute for password confirmation, and the user is still created!
Is this mean that in the :validatable module there is not (?):
validates :password, :confirmation => true
Thanks to get you view on this!
one problem is the trailing comma at the end of your each block. second, you are not asserting anything in your test to pass or fail the test, though you are probably erroring out at this point, which is why you are saying it didnt pass.
you can try assigning the user object to a variable:
it "should create a new instance given valid attributes" do
#user = User.new(#attr)
#user.should be_valid #=> new will let you know if its valid or not
#user.save.should be_true #=> another possible assertion to pass/fail the test
# debug message to give you back why it failed
puts #user.errors.full_messages.to_sentence
end

validates_uniqueness_of don't work

It's not some kind of synchronization problem I readed before.
The code is quite simple.
The model:
class User < ActiveRecord::Base
attr_accessor :name, :email
validates_uniqueness_of :email, :on => :create, :message => "must be unique"
end
The rspec test:
require 'spec_helper'
describe User do
before(:each) do
#valid_attributes = {
:name => "Foo Bar",
:email => "foo#bar.com"
}
end
it "should reject duplcate email address" do
User.create!(#valid_attributes)
duplicate_user = User.new(#valid_attributes)
duplicate_user.should_not be_valid
end
end
I run the test, and get error message:
----------------------------
1)
'User should reject duplcate email address' FAILED
expected #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> not to be valid
/Users/mac/workspace/rails_space/uniq/spec/models/user_spec.rb:14:
Finished in 0.067908 seconds
1 example, 1 failure
-----------------------------
I run the script/console, and create two user objects with same email address. It goes fine, no validate message occur, the two objects both have inserted into the table. I don't what's wrong with it.
My rails version is 2.3.8 and rspc is 1.3.0.
I believe the problem is the attr_accessor line that you have. If you have those column names, the accessor will override the column name and that is just part of the class and doesn't care about uniqueness. If you are going to have the accessor methods then it needs to get back to the database in some way. If you need to have the accessor, then you need to tie it to the database by calling write_attribute.
For more information you can see the documentation for "Overwriting default accessors" at http://api.rubyonrails.org/classes/ActiveRecord/Base.html
I hope this helps!
I think the issue is because you are saying:
validates_uniqueness_of :email, :on => :create
User.new may not be triggering this validation.
Try calling duplicate_user.save! and see if that throws an error.
You can try like following
attr_accessible :email
validates_uniqueness_of :email, :on => :create, :message => "must be unique"

Resources