简体   繁体   中英

Ruby on Rails rspec uniqueness test failing inexplicably

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: "some@guy.com",
  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@user@.com]
      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[hi@iamhere.com yo@yothisisme.com whatup@whatupdog.co.uk]
        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: "user@example.com",
                     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: "some@guy.com", 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: "some@guy.com", password: "happiness", password_confirmation: "happiness")
 => #<User id: nil, name: "Someguy", email: "some@guy.com", 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: "some@guy.com", 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('some@guy.com') 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", "some@guy.com"], ["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('some@guy.com') 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:

    ..........some@guy.comhello
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: "some@guy.com", 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,

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM