【问题标题】:rSpec Shoulda matchers testing validationsrSpec Shoulda 匹配器测试验证
【发布时间】:2013-09-12 00:31:27
【问题描述】:

我对模型的属性进行了以下验证:

validates :on_ride_photo,
  presence: true,
  inclusion: { in: [true, false] }

然后我进行以下测试:

context 'on_ride_photo' do
  it { should validate_presence_of(:on_ride_photo) }
  it { should allow_value(true).for(:on_ride_photo) }
  it { should allow_value(false).for(:on_ride_photo) }
  it { should_not allow_value(nil).for(:on_ride_photo) }
  it { should_not allow_value(4).for(:on_ride_photo) }
  it { should_not allow_value('yes').for(:on_ride_photo) }
end

但在运行我的规范时出现以下错误:

2) Coaster validations should allow on_ride_photo to be set to false
   Failure/Error: it { should allow_value(false).for(:on_ride_photo) }
     Did not expect errors  when on_ride_photo is set to false, got error:
   # ./spec/models/coaster_spec.rb:86:in `block (3 levels) in <top (required)>'

我想允许 false 作为有效值的事实是否被它必须存在并且 false 被归类为不存在的事实所推翻?如果是这样,那么我该如何解决这个问题?

【问题讨论】:

  • 这是一个布尔字段。我将在我的 OP 中发布所有 :on_ride_photo 规格。
  • 关于这个问题的活动很多。请让 Billy 和/或我知道我们的回答是否有帮助/可以接受,或者您是否有任何后续 cmets 或问题。谢谢。

标签: ruby-on-rails testing rspec shoulda


【解决方案1】:

是的,问题在于presence: true,它会检查属性是否不是Object#blank?。由于验证是附加的,inclusion: 子句不能“覆盖”false 未通过 presence 测试的事实。

以下来自http://edgeguides.rubyonrails.org/active_record_validations.html#presence

由于false.blank?true,如果你想验证存在 您应该使用的布尔字段validates :field_name, inclusion: { in: [true, false] }.

有关如何处理布尔字段的讨论,请参阅 Billy Chan's answer,尤其包括有关将哪些值视为 niltruefalse 的信息。

【讨论】:

  • 好的,有道理。但是,当使用它时,这两个规范失败了: it { should_not allow_value(4).for(:on_ride_photo) } it { should_not allow_value('yes').for(:on_ride_photo) }`
  • 所以说 4 和“是”都被归类为 true 并且应该被允许但是测试失败了,因为当测试试图确保它们失败时,它们被允许?有没有办法解决这个问题?
  • 是的,没错。我不确定你所说的“解决这个问题”是什么意思,但我会根据属性允许的 input 来考虑 allow_value 方法,在这种情况下为 4 和 'yes ' 是允许的,你的例子应该反映这一点。
  • 嗯,我只想接受真假,4 和“是”应该失败 - 这就是我希望它的工作方式,只是完全不确定如何获得该结果。目前,如果有人输入 4 那么它将变成一个真实的值,我不希望那样。这就是我卡住的地方。
  • 这是一个 PostgreSQL 数据库
【解决方案2】:

终于得到了答案,感谢彼得的更新。

  1. 要在迁移中设置布尔值,请使用t.boolean :foo
  2. 要验证布尔值,请使用 Peter 的回答中推荐的验证器。
  3. 要了解哪些值将用作truefalse 值,请参见下文。 (我认为没有必要对它们进行单元测试,尽管包含其中一部分的集成或功能测试是可以的)

我刚刚浏览了源代码。 boolean 类型,无论是 PostgreSQL、Sqlite3 还是 MySQL,最终都会指向ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)

    # convert something to a boolean
    def value_to_boolean(value)
      if value.is_a?(String) && value.empty?
        nil
      else
        TRUE_VALUES.include?(value)
      end
    end

然后被这个常数判断

  # the namespace is still "ActiveRecord::ConnectionAdapters::Column"
  TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
  FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set

所以,我们终于明白了。

  1. 如果为空,则无

  2. 如果不为空,检查是否为TRUE_VALUE,如果不为false。这就是为什么“4”、“是”是假的。

【讨论】:

  • 干得好,找到源代码,Billy。不仅发现了我错过的true 值,而且指出“”被视为零。我将删除答案的后半部分并参考您的答案。不过,仍然想知道如何使用验证来拒绝非直观的输入值(例如,任何不在 TRUE_VALUESFALSE_VALUES 中的东西)。
  • @PeterAlfvin,我认为拒绝非直觉值是可能的,方法是检查值是否在这两个常量的连接中,类似于inclusion: {in: combined_enum }。但我认为这有点矫枉过正:)
  • 鉴于整个线程,我认为inclusion: {in: ...}} 在强制发生后进行检查。如果您想拒绝任何非空值,则需要以其他方式进行。
猜你喜欢
  • 2012-11-28
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
  • 2015-01-18
相关资源
最近更新 更多