【问题标题】:Using shoulda matchers to test `allow_nil` in combination with `uniqueness` and `scope`使用 shoulda 匹配器结合 `uniqueness` 和 `scope` 来测试 `allow_nil`
【发布时间】:2015-12-02 04:18:37
【问题描述】:

我正在为存储键/值对的表构建 ActiveRecord 模型

例子-

|------------------------------|
| KEY      | VALUE             |
|----------|-------------------|
| LOCATION | San Francisco, CA |
| TITLE    | Manager           |
| LOCATION | New York City, NY |
|------------------------------|

这是模型 -

class CompanyEnum < ActiveRecord::Base
  KEYS = [:title, :department, :location]
  KEYS_ENUM = KEYS.map(&:to_s).map(&:upcase)

  # `key` column must be one of the above - LOCATION, DEPARTMENT, or TITLE
  validates(:key, inclusion: KEYS_ENUM, allow_nil: false)

  # `value` can be anything, but must be unique for a given key (ignoring case) 
  validates(
    :value,
    uniqueness: { scope: :key, case_sensitive: false },
    allow_nil: false
  )
end

我正在使用shoulda matchers 为这些验证编写规范。所以在我的规范文件中,我有以下两个规范 -

describe "validations" do
  it { should_not allow_value(nil).for(:key) }
  it { should_not allow_value(nil).for(:value) }
end

我的问题是:key 的第一次验证通过,但:value 的第二次验证失败。根据模型定义,两者都使用相同的 allow_nil: false 选项。

1) CompanyEnum validations value should not allow value to be set to nil
   Failure/Error: it { should_not allow_value(nil).for(:value) }
     Expected errors when value is set to nil,
     got no errors
   # ./spec/models/company_enum_spec.rb:13:in `block (4 levels) in <top (required)>'
   # ./spec/support/analytics.rb:4:in `block (2 levels) in <top (required)>'

allow_nil: falseuniqueness:scope: optoins 一起使用是否有任何逻辑问题?或者它与我命名实际列 :key:value 有什么关系(因为这些似乎通用到与其他一些方法冲突)?

谢谢!

【问题讨论】:

  • 为什么不直接使用存在验证器呢?我认为它因为范围条款而搞砸了。也只是一个性能提及,但您可能希望冻结每个字符串和键枚举的数组,否则它们是使用每个模型实例创建的。

标签: ruby-on-rails activerecord shoulda


【解决方案1】:

allow_nil: false 进行唯一性验证并不意味着 nil 不是允许的值。这有点误导。

您可能已经知道,默认情况下,唯一性验证以这种方式工作(根据您使用的代码判断):

  • 如果有两条记录具有相同的值keyvalue,那么第二条记录与第一条记录相比应该是无效的(前提是数据库中存在第一条记录)。
  • 如果有两条记录具有不同的值keyvalue,那么两条记录都应该有效。

那么allow_nil 做了什么?来看看the docs怎么说:

:allow_nil - 如果设置为true,如果属性为nil(默认为false)则跳过此验证。

因此,如果为真,allow_nil 允许两条记录与正在验证的属性的 nil 值共存(在本例中为 value)。

但是你指定了allow_nil: false,这意味着这个选项无论如何都不适用。

总之,您第一次使用allow_value 失败,因为nil 不是key 的有效值(包含验证失败)。但是,第二次使用通过了,因为 nilvalue 的有效值(前提是不存在同时具有 valuenil 的现有记录)。

我想我的观点是,如果你真的想测试你的验证,你应该使用直接对应这些验证的匹配器:

it do
  should validate_inclusion_of(:key).
    in_array(["TITLE", "DEPARTMENT", "LOCATION"]).
    allow_nil
end

it do
  should validate_uniqueness_of(:value).
    scoped_to(:key).
    case_insensitive
end

【讨论】:

  • 这很有意义,谢谢!我猜allow_nil 的目的不是描述该字段的值,而是标记天气验证本身应该为nil 值运行。正如您提到的,这似乎有点误导,但感谢您清理它
猜你喜欢
  • 2013-08-10
  • 1970-01-01
  • 2013-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-18
  • 1970-01-01
相关资源
最近更新 更多