当我以这种方式使用FactoryGirl时,为什么我的测试失败了?

时间:2012-09-13 23:12:24

标签: ruby-on-rails ruby-on-rails-3 factory-bot rspec-rails

我正在测试我的用户模型并研究FactoryGirl的工作原理。当我在 user_spec.rb

中执行此操作时
before(:each) do
  @user = User.new(username: 'ExampleUser', email: 'user@example.com', timezone: 'Eastern Time (US & Canada)', password: 'example')
end

一切都过去了,但如果我这样做了:

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

它未通过测试以查看用户的用户名和电子邮件是否已被删除。

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

这些是测试:

  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

有人可以解释一下吗?我以为FactoryGirl想让我像User.new一样使用它,这是我的第一个有效的例子。

2 个答案:

答案 0 :(得分:1)

FactoryGirl.create实际创建了记录,而User.new仅实例化模型,但实际上并未保存记录。

如果您只想实例化模型,则应使用FactoryGirl.build

before(:each) do
  @user = FactoryGirl.build(:user)
end

有关详细信息,请参阅documentation

因此,我认为当前代码发生的情况是,当您使用FactoryGirl.create创建用户时,它实际上保存了记录而没有验证问题(因为尚未创建副本)。当您使用user_with_same_email.save的同一电子邮件保存用户时,它实际上并不保存该用户,但您没有看到。然后,当您检查原始用户是否有效时,它会显示是,因为您在尝试(并且未能)创建副本之前已经保存了它。

有意义吗?无论如何只需切换到FactoryGirl.build,两个测试都应该通过。

答案 1 :(得分:1)

通常在使用具有validates_uniqueness_of的Factory Girl测试字段时,最好使用sequence

使用序列时,每次使用FactoryGirl.create(:user)创建记录时,用户名始终是唯一的。这使您可以使用数据库中的“真实”记录,而无需手动纠正冲突的值。

factory :user do
  sequence :username do |n}
    "user_#{n}"
  end
end

注意:我不喜欢测试尚未添加到数据库中的记录的想法。我想不出有什么可能成为问题的坚实理由。我能想到的唯一问题是你将无法测试关联。


我一直注意到的其他问题是您使用before块并创建实例变量。在RSpec中,有一个名为let的方法将在需要时创建变量。

这会使您的user_spec.rb文件正常工作。

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还有一个bang方法,可以创建变量,无论它是否在it块中使用。