2

我正在测试我的用户模型并研究 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,这是我的第一个有效示例。

4

2 回答 2

1

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

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

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

有关详细信息,请参阅文档

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

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

于 2012-09-13T23:43:02.093 回答
1

通常,在使用 Factory Girl 测试字段时,validates_uniqueness_of最好使用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块中使用,它都会创建变量。

于 2012-09-14T02:40:37.010 回答