Reputation: 626
I have a suite of test for my @user model (following mhartl's tutorial) but inexplicably my test for email uniqueness has started failing mysteriously
require 'spec_helper'
describe User do
before { @user = User.new(name: "Someguy", email: "[email protected]",
password: "password", password_confirmation: "password")}
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 be_valid }
describe "when name is not present" do
before {@user.name = ""}
it {should_not be_valid}
end
describe "when email is not present" do
before {@user.email = ""}
it {should_not be_valid}
end
describe "when name is too long" do
before {(@user.name = "h"*50)}
it {should_not be_valid}
end
describe "when email format is invalid" do
it "should be invalid" do
addresses = %w[rubbishemail@foo,com rubbishuser_at_foo.org rubbish@[email protected]]
addresses.each do |i|
@user.email = i
expect(@user).not_to be_valid
end
end
end
describe "when email format is valid" do
it "should be valid" do
addresses = %w[[email protected] [email protected] [email protected]]
addresses.each do |i|
@user.email = i
expect(@user).to be_valid
end
end
end
describe "when email is already taken" do
it "should_not be valid" 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 password is blank" do
before do
@user = User.new(name: "Example User", email: "[email protected]",
password: " ", password_confirmation: " ")
end
it { should_not be_valid }
end
describe "when password is not the same as password_confirmation" do
before {@user.password_confirmation = "mismatch"}
end
describe "with a password that's too short" do
before { @user.password = @user.password_confirmation = "a" * 5 }
it { should be_invalid }
end
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 eq found_user.authenticate(@user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not eq user_for_invalid_password }
specify { expect(user_for_invalid_password).to be_false }
end
end
end
The specific test that is failing is:
describe "when email is already taken" do
it "should_not be valid" 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
And the error I am getting is as follows:
Failures:
1) User when email is already taken should not be valid
Failure/Error: it {should_not be_valid}
expected #<User id: nil, name: "Someguy", email: "[email protected]", created_at: nil, updated_at: nil, password_digest: "$2a$04$ijkjuSSyFBAaMsDQ5vOyFO2VBVKFfFuvy5ArvfcWdW4H...", remember_token: nil> not to be valid
# ./spec/models/user_spec.rb:60:in `block (3 levels) in <top (required)>'
The problem doesn't seem to be with the user model itself since if I go to the console and try to manually reconstruct the test I get the expected result (user.valid? returns false after saving user.dup.
2.0.0-p247 :007 > user = User.new(name: "Someguy", email: "[email protected]", password: "happiness", password_confirmation: "happiness")
=> #<User id: nil, name: "Someguy", email: "[email protected]", created_at: nil, updated_at: nil, password_digest: "$2a$10$iGXz/r/RsiLKtNydXKft/en3KL.zT1B/bD5VehfNhiUj...", remember_token: nil>
2.0.0-p247 :008 > user2 = user.dup
=> #<User id: nil, name: "Someguy", email: "[email protected]", created_at: nil, updated_at: nil, password_digest: "$2a$10$iGXz/r/RsiLKtNydXKft/en3KL.zT1B/bD5VehfNhiUj...", remember_token: nil>
2.0.0-p247 :009 > user2.save
(0.2ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('[email protected]') LIMIT 1
Binary data inserted for `string` type on column `password_digest`
SQL (3.9ms) INSERT INTO "users" ("created_at", "email", "name", "password_digest", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", Sun, 20 Oct 2013 00:30:45 UTC +00:00], ["email", "[email protected]"], ["name", "Someguy"], ["password_digest", "$2a$10$iGXz/r/RsiLKtNydXKft/en3KL.zT1B/bD5VehfNhiUj0i2Xl0YiK"], ["updated_at", Sun, 20 Oct 2013 00:30:45 UTC +00:00]]
(2.2ms) commit transaction
=> true
2.0.0-p247 :010 > user.valid?
User Exists (0.4ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('[email protected]') LIMIT 1
=> false
2.0.0-p247 :011 >
The code for the user model is below (although I do not think this is the problem?)
class User < ActiveRecord::Base
before_save {self.email = email.downcase }
validates(:name, {presence: true, length: {maximum: 49}})
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[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 }
has_secure_password
end
I tried to identify where the test was failing by adding some puts into the test as follows
describe "when email is already taken" do
it "should_not be valid" do
user_with_same_email = @user.dup
puts @user.email+"hello"
user_with_same_email.email = @user.email.upcase
did_save = user_with_same_email.save
puts did_save
end
it {should_not be_valid}
end
and the result when running the test suite is:
[email protected]
true
.F........
Failures:
1) User when email is already taken should not be valid
Failure/Error: it {should_not be_valid}
expected #<User id: nil, name: "Someguy", email: "[email protected]", created_at: nil, updated_at: nil, password_digest: "$2a$04$UKI8WbXbK9QnG7BF6AGnpeteaM8llDB/NMUj07UMTYsX...", remember_token: nil> not to be valid
# ./spec/models/user_spec.rb:62:in `block (3 levels) in <top (required)>'
Finished in 0.17485 seconds
20 examples, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:62 # User when email is already taken should not be valid
from the above it looks as though the @user.email object is nil! But this can't be the whole story because @user.dup is successfully assigning the correct values...
Can anyone explain why the test is failing?
THanks,
Upvotes: 0
Views: 575
Reputation: 2750
I think you need to use a before
block here as opposed to two it
blocks (the first block contains no test, and the second block will not have had the duplicate user saved before it is run).
describe "when email 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
Upvotes: 1