我有一个ActiveRecord模型,它具有唯一性验证:
class Profile < ActiveRecord::Base
# fields: :name, :slug
before_validation :set_default_slug_from_name
validates :slug, uniqueness: {case_sensitive: false},
unless: -> { |p| p.slug.blank? }
# ...
end
现在,在编写此模型的规范时,我想模拟唯一性验证错误而不会访问数据库,这样我就可以测试一些取决于这种错误的模型行为:
describe Profile
before { subject.name = "Bob Greenfield" }
it "modifies the beginning of the slug if there is a duplicate" do
# simulate uniqueness conflict (duplicate record) here
subject.valid?
expect(subject.slug).to match(/^\w+-bob-greenfield$/)
end
end
我挖掘了实现UniquenessValidator
的{{3}}并尝试了以下内容:
allow_any_instance_of(ActiveRecord::Relation).to receive(:exists?).and_return(true)
# ...
但这似乎不起作用。
答案 0 :(得分:3)
我想你在这里试图有点太深了。虽然阅读rails源并了解其工作原理非常棒,但是模拟Rails内部使用的每个类实例并不是一个好主意。在这种情况下,与其他验证器相反,UniquenessValidator确实依赖于数据库,因此您应该:
一个。允许自己点击数据库。在这种情况下,这不是一个巨大的开销,也可能是实现这一目标的最务实的方法。你甚至使用&#34;重复记录&#34;在您的规范中的评论中,为什么不在那里有记录?在10个案例中的9个案例中,这是正确的方法。毕竟你正在检查一种ActiveRecord :: Base对象。
湾检查独立于rails验证的行为。无论如何应该这样做来测试你喜欢的slug格式。最好在确认之前确保默认slug有效。 这里的缺点是,在这次调用和实际验证之前,很少有机会被其他人占用,但是Rails建立在验证中是一样的,所以我不担心它 - 你最终会出错,接下来的尝试很可能会成功。
it "modifies the beginning of the slug on validation if there is a duplicate" do
Profile.stub(:slug_exists?).with("bob-greenfield").and_return(true)
Profile.stub(:slug_exists?).with("1-bob-greenfield").and_return(true)
Profile.stub(:slug_exists?).with("2-bob-greenfield").and_return(false)
subject.set_default_slug_from_name
subject.slug.should == "2-bob-greenfield"
end
并且您已经知道Rails在验证之前调用,因此您不必测试此行为。
此外,最好确保您对数据库有约束,Rails唯一性是不够的:http://robots.thoughtbot.com/the-perils-of-uniqueness-validations