ruby-on-rails – Railstutorial.org验证唯一的电子邮件

Ruby on Rails 3教程的6.2.4节中,Michael Hartl描述了一个关于检查电子邮件地址唯一性的警告:如果两个相同的请求及时接近,请求A可以通过验证,然后B通过验证,然后A得到保存,然后B得到保存,你得到两个具有相同值的记录.每个都在检查时有效.

我的问题不在于解决方案(对数据库设置了一个独特的约束,因此B的保存不起作用).这是关于编写测试以证明解决方案有效.我尝试自己编写,但无论我想出什么,只能模仿常规的,简单的唯一性测试.

作为rspec的全新,我的天真方法是编写场景:

it 'should reject duplicate email addresses with caveat' do
  A = User.new( @attr ) 
  A.should be_valid          # always valid

  B = User.new( @attr )
  B.should be_valid          # always valid, as expected

  A.save.should == true      # save always works fine

  B.save.should == false     # this is the problem case
  # B.should_not be_valid    # ...same results as "save.should"
end

但是这个测试在与常规唯一性测试完全相同的情况下通过/失败;编写代码时,B.save.should == false会传递,以便常规测试失败时常规唯一性测试通过并失败.

所以我的问题是“如何编写一个可以验证我正在解决这个问题的rspec测试?”如果答案结果是“它很复杂”,那么我应该看一下不同的Rails测试框架吗?

最佳答案
情况很复杂.竞争条件非常糟糕,因为它们很难再现.在内部,保存就是这样的:

>验证.
>写入数据库.

因此,要重现计时问题,您需要安排两个保存调用重叠,如下所示(伪Rails):

a.validate    # first half of a.save
b.validate    # first half of b.save
a.write_to_db # second half of a.save
b.write_to_db # second half of b.save

但是你不能打开保存方法并且非常容易地摆弄它的内部结构.

但是(这是一个很大但是),you can skip the validations entirely

Note that save also has the ability to skip validations if passed :validate => false as argument. This technique should be used with caution.

所以,如果你使用

b.save(:validate => false)

你应该只获得“写入数据库”b的一半保存并将数据发送到数据库而无需验证.这应该会触发数据库中的约束违规,我很确定会引发ActiveRecord::StatementInvalid异常,所以我认为你需要查找异常,而不仅仅是从save中返回错误:

b.save(:validate => false).should raise_exception(ActiveRecord::StatementInvalid)

您可以将其收紧,以查找特定的异常消息.我没有任何方便测试此测试,因此请在Rails控制台中尝试并适当调整您的规范.

转载注明原文:ruby-on-rails – Railstutorial.org验证唯一的电子邮件 - 代码日志