跳过before_validation :: test in test

时间:2014-01-24 17:40:52

标签: ruby-on-rails callback before-filter

我遇到了测试一个相当简单的问题的问题,即数据库中不允许存在双重代码。

假设我有这个模型:

class Ticket < ActiveRecord::Base
  attr_accessible :code, :series, :sec_code
  validates_uniqueness_of :code, scope:[:series,:sec_code]

  before_validation :generate_codes, on: :create  


  private
  def generate_codes
    self.code=rand.to_s[2..9]  #-> 8 digit code
    self.sec_code=rand.to_s[2..4] #-> 2 digit security code
    self.series=('A'..'Z').to_a.sample #-> one character series
  end

end

然后我想以这种方式测试唯一性验证是否有效:

require 'spec_helper'

describe Ticket do
  before do
    @ticket=Ticket.create
  end
  subject{@ticket}

  it{ should respond_to(:code)}
  ...

  describe ", duplicate codes" do
    before do
      dup_ticket=@ticket.dup
      #Here lays the problem since this calls `generate_codes` 
      #before saving, so it ends up not being a duplicate!
      dup_ticket.save  #<--- 
    end
    it{ should_not be_valid}
  end

end

我正在考虑使用after_initialize,但每次实例化对象时都会调用它,即在从DB中提取后调用,这是不希望的。 我想知道,即使在过滤器回调中有generate_codes,为什么on: :create也会被调用?

2 个答案:

答案 0 :(得分:0)

您可以尝试对generate_codes方法进行存根以返回您想要的内容。所以可能会添加:

allow(dup_ticket).to receive(:generate_codes).and_return(nil)

我无法对此进行测试,但这可能会阻止记录生成新值。我对dup并不完全熟悉,因此您需要确保dup也包含代码值。

答案 1 :(得分:0)

jklina 的回答/评论的启发,我发现在这种情况下使用update_attribute会有所帮助,我可以更新字段而不会触发之前的验证回调。 所以测试变成了:

require 'spec_helper'

describe Ticket do
  before do
    @ticket=Ticket.create
  end
  subject{@ticket}

  it{ should respond_to(:code)}
  ...

  describe ", duplicate codes" do
    before do
      dup_ticket=@ticket.dup
      dup_ticket.update_attribute(:code, @ticket.code)
      dup_ticket.update_attribute(:sec_code, @ticket.sec_code)
      dup_ticket.update_attribute(:series, @ticket.series)
    end
    it{ should_not be_valid}
  end

end

因此,在ActiveRecord表中反映codesec_codeseries的相同值。测试完成了它预期的目标。