我遇到了测试一个相当简单的问题的问题,即数据库中不允许存在双重代码。
假设我有这个模型:
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
也会被调用?
答案 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表中反映code
,sec_code
和series
的相同值。测试完成了它预期的目标。