ruby on rails-如何在没有数据库的情况下模拟ActiveRecord模型规范中的唯一性验证错误



我有一个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的Rails代码,并尝试了以下内容:

allow_any_instance_of(ActiveRecord::Relation).to receive(:exists?).and_return(true)
# ...

但这似乎并不奏效。

我觉得你在这里想得有点太深了。虽然阅读rails的源代码并了解这些东西是如何工作的很好,但嘲笑rails内部使用的每个类实例并不是一个好主意。在这种情况下,与其他一些验证器不同,UniquenessValidator确实依赖于数据库,因此您应该:

a。允许自己访问数据库。在这种情况下,这并不是一个巨大的开销,可能也是完成这项工作的最务实的方式。您甚至在规范中的注释中使用了"重复记录",所以为什么不在那里有一个记录呢?在十分之九的情况下,这是正确的做法。毕竟,您正在检查一种ActiveRecord::Base对象。

b。检查独立于rails验证的行为。无论如何都应该这样做,以测试您喜欢的slug格式。在验证之前最好确保默认的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

最新更新