Railstutorial.org验证唯一的电子邮件

时间:2012-01-04 05:46:28

标签: ruby-on-rails rspec rspec-rails railstutorial.org

Ruby on Rails 3 Tutorial 的第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测试框架吗?

1 个答案:

答案 0 :(得分:4)

这很复杂。竞争条件非常糟糕,因为它们很难再现。在内部,save就是这样的:

  1. 验证
  2. 写入数据库。
  3. 因此,要重现计时问题,您需要安排两个save调用重叠,如下所示(伪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
    

    但是你不能打开save方法并且很容易摆弄它的内部。

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

      

    请注意,如果将save作为参数传递,:validate => false也可以跳过验证。应谨慎使用此技术。

    所以如果你使用

    b.save(:validate => false)
    

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

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

    您可以加强它以查找特定的异常消息。我没有任何方便测试这个测试,所以在Rails控制台中试试并适当调整你的规范。

相关问题