如何使用 RSpec 在 Rails 中测试 :inclusion 验证

46

我在我的ActiveRecord中有以下验证。

validates :active, :inclusion => {:in => ['Y', 'N']}

我正在使用以下内容来测试我的模型验证。

should_not allow_value('A').for(:active)
should allow_value('Y').for(:active)
should allow_value('N').for(:active)

有没有更干净、更全面的测试方法?我目前正在使用RSpec2和shoulda matchers。

编辑

经过一些搜索,我发现只有这种可能是“好”的测试方法,shoulda没有为此提供任何东西,任何需要它的人都可以为其编写自己的自定义匹配器(并可能将其贡献回项目)。一些可能有趣的讨论链接:

  • 上述内容的链接。 Link 1Link 2

  • should_ensure_value_in_range 这个接近于可以使用的内容,但仅接受范围而不是值列表。自定义匹配器可以基于此。

4个回答

78

使用 shoulda_matchers

在最近的版本中 (shoulda-matchers 至少从 v2.7.0 开始),您可以这样做:

expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N])

这个测试确保验证中可接受的值数组完全符合规范。

在早期的版本(>= v1.4),shoulda_matchers 支持以下语法:

it {should ensure_inclusion_of(:active).in_array(%w[Y N]) }

1
为了检查是否禁止其他值,您可以执行以下操作:it { should_not allow_value('?').for(:active) } -- 就像您所说的那样,您无法检查所有可能的值,但是除了检查所有允许的值之外,这样做似乎是合理的覆盖范围。 - bjnord
1
@LarsLevie - 感谢您的评论。看起来他们已经更改了验证以检查disallows_value_outside_of_array?。请参见旧版 https://github.com/thoughtbot/shoulda-matchers/blob/v1.2.0/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb#L86 与现在的 https://github.com/thoughtbot/shoulda-matchers/blob/15abdf066732828034efea751c2937aa81d080fe/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb#L90 - Nathan Long
1
shoulda-matchers v2.7.0开始,此语法已被弃用。新的首选语法使用RSpec 3.0 expect语法,如下所示:expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N]) - Nathan Wallace
1
匹配器的变化是它不再是ensure_inclusion_of,现在是validate_inclusion_of - Nathan Wallace
2
顺便提一下,当使用这个布尔值来运行测试套件时,我收到了以下警告:shoulda-matchers的警告:您正在使用“validate_inclusion_of”来断言布尔列允许布尔值并禁止非布尔值。请注意,无法完全测试此功能,因为布尔列将自动将非布尔值转换为布尔值。因此,您应该考虑删除此测试。 - Joshua Pinter
显示剩余3条评论

26

如果你需要测试的元素不止是一个布尔值 Y/N,那么你也可以尝试:

it "should allow valid values" do
  %w(item1 item2 item3 item4).each do |v|
    should allow_value(v).for(:field)
  end
end
it { should_not allow_value("other").for(:role) }

你也可以用你在模型中定义的常量替代%w(),这样就可以测试只允许常量值。

CONSTANT = %w[item1 item2 item3 item4]
validates :field, :inclusion => CONSTANT

然后是测试:

it "should allow valid values" do
  Model::CONSTANT.each do |v|
    should allow_value(v).for(:field)
  end
end

1
我发现在我正在工作的一个项目中有一个自定义的shoulda匹配器,它试图接近测试类似于这样的东西:
例子:
it { should validate_inclusion_check_constraint_on :status, :allowed_values => %w(Open Resolved Closed) }
it { should validate_inclusion_check_constraint_on :age, :allowed_values => 0..100 }

匹配器试图确保在尝试保存时会出现DB约束。我将尝试给出这个想法的本质。matches?实现类似于:

  begin
    @allowed_values.each do |value|
      @subject.send("#{@attribute}=", value)
      @subject.save(:validate => false)
    end
  rescue ::ActiveRecord::StatementInvalid => e
    # Returns false if the exception message contains a string matching the error throw by SQL db
  end

我猜如果我们稍微改变上面的代码,使用@subject.save并让Rails验证失败,当异常字符串包含接近真实异常错误消息的内容时,我们可以返回false。

我知道这离完美还有很大差距,但如果你真的想测试很多:inclusion验证,把它作为自定义匹配器添加到你的项目中可能也不是个坏主意。


我将接受自己的答案,因为似乎找不到其他解决方案,但请随意批评上述解决方案可能失败的方式,或者是否值得费心去做这件事。 - jake

0
在 shoulda-matchers >= 5 中,您应该能够使用 :validate_inclusion_of
it { should validate_inclusion_of(:active).in_array(%w[Y N]) }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接