Mike Hartl's book gives the below for Listing 6.17 for a test for the rejection of duplicate email addresses, insensitive to case.
The call to User.new creates an in memory user but nowhere is #user.save called to save it to the database.
Running the test, it correctly rejects the duplicate address. But I can't figure out how it works. Only user_with_same_email is saved to the database not #user. So how could the validity test detect the duplicate without #user saved?
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
subject { #user }
.
.
.
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
end
user_with_same_email is actually saved in the database.
Notice that subject is #user.
subject { #user }
Hence, example it { should_not be_valid } is executed on implied subject which is #user.
#user.valid? returns false as you have put uniqueness constraint on the email address and there is already a record with same email address in your database(recall user_with_same_email that was saved).
So, your test example passes as #user is not valid.
The object #user need not be saved to run the validity check. The rspec expression it { should_not be_valid } calls #user.valid? under the hood. This method can be run on an ActiveRecord instance whether or not it has been saved.
Related
I'm new to all this rails/rspec and so on, so im trying to follow some tests that I had found in the project that i'm working and make sense, they were generated by scaffold but the app is very different and no tests were updated so im re-doing every test.
The problem is when I try to validate that invalid emails should make the user invalid it fails.
I know that it's ok because I already done tests and they pass without no problem.
It is possible that I'm looking at this the wrong way but ...
User_spec
describe "when email format is invalid" do
it "should be invalid" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.
foo#bar_baz.com foo#bar+baz.com]
addresses.each do |invalid_address|
#user.email = invalid_address
#user.should_not be_valid
end
end
end
User_test
test "fail save new user with an corrupt email" do
user = User.new
user.name = 'Someone Name'
user.password = '12345678'
user.email = 'Someone Name#gmail..com'
assert !user.save , "User was saved with an corrupt email"
end
The validation is done by devise and the failure message is "expected valid? to return false, got true"
---- edit ---
My user setup
...
describe User do
before :each do
#user = User.new(name: "Name of Names", email:"someone1#somewhere.com",password: "foobarfoobar", password_confirmation: "foobarfoobar" )
end
...
You could do something along the lines of this:
context "validations" do
describe "email" do
subject { FactoryGirl.create(:user, email: email) }
context "with an valid address" do
let(:email) { "valid#email.com" }
it { should be_valid }
end
context "with an invalid email address" do
let(:email) { "invalid.email.com" }
it { should_not be_valid }
end
end
end
So I'm assuming you are using FactoryGirl to create an valid user object. Just create new tests per invalid email, this way you can see which tests are passing and which aren't. Then you could TDD your way to green.
Here is my Spec file:
require 'spec_helper'
describe User, "references" 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) }
end
describe User, "factory" do
before(:each) do
#user = FactoryGirl.create(:user)
end
it "is invalid with no email" do
#user.email = nil
#user.should_not be_valid
end
it "is valid with email" do
#user.should be_valid
end
end
Factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.email
password "password"
password_confirmation "password"
agreed_to_age_requirements true
end
end
The part I am trying to "test" for and not sure how to 100% is checking to make sure when a User is created that the email address is not nil.
shoulda provides validation helpers to help you test the validations.
it { should validate_presence_of(:email) }
If you want to use rspec and write your own, then
describe User do
it "should be invalid without email" do
user = FactoryGirl.build(:user, :email => nil)
#user.should_not be_valid
#user.errors.on(:email).should == 'can't be blank' #not sure about the exact message. But you will know when you run the test
end
it "should be valid with email" do
user = FactoryGirl.build(:user, :email => "user#user.com")
#user.should be_valid
end
end
When you run the test, it would read as
User
should be invalid without email
should be valid with email
Giving a good description for your test case is very important, because it kind of acts like a documentation.
The listing 6.20 of Michael Hartl's rails tutorial shows the following code:
before do
#user = User.new(name: "Example User", email: "user#example.com")
end
.
.
.
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
I am having trouble grasping this concept because #user.dup returns a representation of the exact same object, which is copied over to user_with_same email, but #user was never saved into the database anywhere in the file. Therefore, the user_with_same_email.save test should be valid every time. However, the test passes. Someone please explain this... is there an implicit database save on #user = User.new(...)? I know if it was User.create(...) there would be a save, but not for the new method. Thanks!
You're not missing an implicit save.
user_with_same_email does save correctly (personally I would always use save! to be sure it was not failing silently)
What the spec is speccing is that the subject (ie #user) cannot be saved, because of the existance of a row in the database with the same email.
I am doing the Ruby on Rails Tutorial and am up to Listing 6.29. It describes the testing of a (seemingly standard) user authentication process.
My problem is understanding the following (edited) part of the user_spec.rb file:
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { #user }
describe "return value of authenticate method" do
before { #user.save }
let(:found_user) { User.find_by_email(#user.email) }
describe "with valid password" do
it { should == found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
end
end
My main confusion is the line:
before { #user.save }
Does this really save the test user in the database? Won't saving this test user before testing the correctness of its password make the test redundant? It looks to me like I'm merely saving the user (with its password) and then checking if it still has the same password (with which I just saved it!) Is someone able to clarify why I'm mistaken?
Yes, it really does save the user in the database (most likely to be cleaned out by database_cleaner, or something, before the next test - tests are typically meant to be mostly run in isolation from one another and do not usually perpetuate state).
Contrary to making the test redundant, it is a required element. The test in question is for the authenticate method, not user creation. The user is being created in order to test the authenticate method against it. Basically, what is happening here is that it is creating the user, and then trying to authenticate that same user with first a valid password, and next and invalid password, to ensure the proper functionality of the authenticate method.
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: 'user#example.com', 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.
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.
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.