Reputation: 27242
I'm testing my User model and studying how FactoryGirl works. When I do this in my user_spec.rb:
before(:each) do
@user = User.new(username: 'ExampleUser', email: '[email protected]', timezone: 'Eastern Time (US & Canada)', password: 'example')
end
Everything passes, but if I do:
before(:each) do
@user = FactoryGirl.create(:user)
end
It fails the test to see if the user's username and email are taken already.
1) User when username is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/user_spec.rb:151:in `block (3 levels) in <top (required)>'
2) User when email address is already taken
Failure/Error: it { should_not be_valid }
expected valid? to return false, got true
# ./spec/models/user_spec.rb:142:in `block (3 levels) in <top (required)>'
Finished in 1.8 seconds
29 examples, 2 failures
These are the test:
describe 'when email address is already taken' do
before do
user_with_same_email = @user.dup
user_with_same_email.email = @user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
describe 'when username is already taken' do
before do
user_with_same_username = @user.dup
user_with_same_username.username = @user.username.upcase
user_with_same_username.save
end
it { should_not be_valid }
end
Can someone explain? I thought FactoryGirl was suppose to let me use it like User.new
, my first example which works.
Upvotes: 2
Views: 462
Reputation: 4010
Generally when testing a field, using Factory Girl, that has validates_uniqueness_of
it's best to use a sequence.
When using a sequence, every time you create a record with FactoryGirl.create(:user)
, the username will always be unique. This allows you to work with "real" records in your database without having to manually correct for conflicting values.
factory :user do
sequence :username do |n}
"user_#{n}"
end
end
Note: I don't like the idea of testing records that haven't been added to the database. I can't think of any solid reason why it would be a problem. The only problem I can think of is the fact that you won't be able to test associations.
Something else that I keep noticing with your questions is you use a before
block and create instance variables. In RSpec there is a method called let
will create the variable when it is needed.
This would make your user_spec.rb
file work like so.
describe User do
let(:user) { create(:user, :first_name => "John", :last_name => "Doe") }
it "should get full name" do
user.full_name.should == "John Doe"
end
end
let
also has a bang method that will create the variable whether it is used in the it
block or not.
Upvotes: 1
Reputation: 27374
FactoryGirl.create
actually creates the record, whereas User.new
only instantiates the model but does not actually save the record.
If you want to only instantiate the model, you should use FactoryGirl.build
:
before(:each) do
@user = FactoryGirl.build(:user)
end
See the documentation for details.
So what I think is happening with your current code is that when you create the user with FactoryGirl.create
, it actually saves the record with no validation issues (since the duplicate has not been created yet). When you save the user with the same email with user_with_same_email.save
, it does not actually save that user but you don't see that. Then when you check if the original user is valid, it says yes because you already saved it before trying (and failing) to create the duplicate.
Make sense? Anyway just switch to FactoryGirl.build
and both tests should pass.
Upvotes: 1