rspec test failure when switching from initializin

2019-07-06 03:00发布

问题:

This makes no sense to me, hope someone can help. I'm getting a single test failure when creating a user using a factory (seemingly similar tests pass).

In my user_spec.rb test file, the user was originally created like this. Using this initialization approach, all tests pass.


USER CREATION

user_spec.rb (without factory)

describe User do

  before do
  @user = User.new(name: "Example User", email: "user@example.com", 
                   password: "foobar", password_confirmation: "foobar", 
                   username: "username")
  end

Most of the other tests for the app use factories. So, decided to replace the above with call to factory.

user_spec.rb (with factory)

describe User do

  before do
  @user = FactoryGirl.create(:user)
  end

FACTORY

The factory looks like this.

factories.rb

FactoryGirl.define do
      factory :user do
        sequence(:name)  { |n| "Person #{n}" }
        sequence(:email) { |n| "person_#{n}@example.com" }
        password              "foobar"
        password_confirmation "foobar"
        sequence(:username) { |n| "username_#{n}" }

TEST FAILURE

Here is the test failure when using the factory to create the user.

user_spec.rb

Failures:

  1) 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:101:in `block (3 levels) in <top (required)>'

Finished in 12.61 seconds
178 examples, 1 failure, 2 pending

TEST

Here is the part of the test that fails when switching to the factory.

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

INSPECTION OF USER AND USER.DUP

My first thought was perhaps using the .dup on the factory created user was causing problems. Here is the @user and the @user.dup when output from their use in the test above (by adding the lines puts @user.inspect and puts user_with_same_email.inspect into the describe block. Both of these puts are after the user_with_same_email.save).

WITHOUT FACTORY (Test passes)

@user

#<User id: nil, name: "Example User", email: "user@example.com", 
created_at: nil, updated_at: nil, 
password_digest: "$2a$04$I/71i.fpTwfp4PqRwAvU4eUEjEkW/wubx6uVBqfNBShq...", 
remember_token: nil, admin: false, username: "username">

user_with_same_email

#<User id: 1178, name: "Example User", email: "USER@EXAMPLE.COM", 
created_at: "2012-04-24 06:01:07", updated_at: "2012-04-24 06:01:07", 
password_digest: "$2a$04$I/71i.fpTwfp4PqRwAvU4eUEjEkW/wubx6uVBqfNBShq...", 
remember_token: "rMS9jM0d4lobLc-A-pTTqA", admin: false, username: "username">

WITH FACTORY (Test fails)

@user

#<User id: 739, name: "Person 67", email: "person_67@example.com", 
created_at: "2012-04-24 05:54:28", updated_at: "2012-04-24 05:54:28", 
password_digest: "$2a$04$n4tToBVM2elz92cfHPKvte6dfHSBj4jDxG.w6DyKtGUR...", 
remember_token: "fy2iifmhXVTFa1__d1dBJg", admin: false, username: "username_67">

user_with_same_email

#<User name: "Person 67", email: "PERSON_67@EXAMPLE.COM", 
created_at: nil, updated_at: nil, 
password_digest: "$2a$04$n4tToBVM2elz92cfHPKvte6dfHSBj4jDxG.w6DyKtGUR...", 
remember_token: "fy2iifmhXVTFa1__d1dBJg", admin: false, username: "username_67">

What stands out above is that @user is not already saved without using the factory, whereas when using the factory @user is saved. I'm just not seeing how this effects the test in this case.

Any help would be appreciated. Thanks!

(The above code comes from Michael Hartl's Rails Tutorial, I'm working on some of the app feature extensions he mentions at the end of the tutorial.)

回答1:

The factory puts the record in the DB. User.new doesn't until it is saved. The original test doesn't check if user_with_same_email gets created, rather that 'it' (subject { @user }) can't be created because the email exists. After making your change to the @user, I had to rewrite the test:

describe "when email address is already taken" do
  let (:user_with_same_email)  { @user.dup }

  specify { user_with_same_email.should_not be_valid }
end


回答2:

Thanks Eric. Building on your test ....

The original test was also testing for case sensitivity as well as uniqueness. This test passes and seems to do the trick. Any thoughts?

describe "when email address is already taken" do
  let(:user_with_same_email) { @user.dup }
  before do 
    user_with_same_email.email = @user.email.upcase 
    user_with_same_email.save
  end
  specify { user_with_same_email.should_not be_valid }
end