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

Posted

技术标签:

【中文标题】如何使用 RSpec 在 Rails 中测试:包含验证【英文标题】:How can I test :inclusion validation in Rails using RSpec 【发布时间】:2011-11-16 22:57:54 【问题描述】:

我的 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 匹配器。

编辑

环顾四周后,我才发现,这可能是一种“好的”测试方式,应该不为此提供任何东西,任何需要它的人都可以为它编写自己的自定义匹配器。(并且可能将其贡献回给该项目)。一些可能有趣的讨论链接:

指向上述内容的链接。 Link 1 , Link 2

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

【问题讨论】:

【参考方案1】:

使用 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]) 

【讨论】:

要检查它是否不允许其他值,您可以执行以下操作:it should_not allow_value('?').for(:active) - 就像您说的,您无法检查所有可能的值,但除了检查所有允许的值之外,还可以这样做似乎是合理的覆盖范围。 @LarsLevie - 感谢您的评论。看起来他们将验证更改为检查disallows_value_outside_of_array?。查看旧的github.com/thoughtbot/shoulda-matchers/blob/v1.2.0/lib/shoulda/… 与现在的github.com/thoughtbot/shoulda-matchers/blob/… 至少从shoulda-matchers v2.7.0 开始,此语法已被弃用。使用 RSpec 3.0 expect 语法的新首选语法是:expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N]) 匹配器的变化是不再是ensure_inclusion_of,现在是validate_inclusion_of 仅供参考,当使用此布尔值运行测试套件时,我收到以下警告:Warning from shoulda-matchers: You are using "validate_inclusion_of" to assert that a boolean column allows boolean values and disallows non-boolean ones. Be aware that it is not possible to fully test this, as boolean columns will automatically convert non-boolean values to boolean ones. Hence, you should consider removing this test.【参考方案2】:

如果您要测试的元素多于布尔 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

【讨论】:

【参考方案3】:

我发现了一个自定义的 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 约束,当它试图保存它时会爆炸。我将尝试给出这个想法的本质。比赛?实现类似于:

  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 验证,作为自定义匹配器添加到你的项目中可能不是一个坏主意。

【讨论】:

我会在这里接受我自己的答案,因为我似乎找不到其他任何东西,但请随时批评上述解决方案如何失败,或者如果打扰这样做甚至是一个坏主意.

以上是关于如何使用 RSpec 在 Rails 中测试:包含验证的主要内容,如果未能解决你的问题,请参考以下文章

使用 RSpec 测试 Rails 辅助方法时如何在参数哈希中设置值?

如何使用rails cucumber,rspec,capybara在视图(dhtml)中测试动态部分?

如何在 Rails/RSpec 中测试异常引发?

由于符号而不是 Rails 导致哈希失败的 RSpec 测试

如何使用 rspec 测试 Cloudinary rails 和 Attachinary?

RSpec:如何测试 Rails 记录器消息期望?