如何测试ActiveRecord中哪些验证失败?

Jan*_*dek 26 testing validation activerecord rspec ruby-on-rails

我有这样的模型:

class User < ActiveRecord::Base
  validates_length_of :name, :in => (2..5)
end
Run Code Online (Sandbox Code Playgroud)

我想测试一下这个验证:

it "should not allow too short name" do
  u = User.new(:name => "a")
  u.valid?
  u.should have(1).error_on(:name)
end
Run Code Online (Sandbox Code Playgroud)

但是它没有测试设置了哪种错误name.我想知道,如果是too_short,too_long,或者一些其它的验证失败.

我可以在errors数组中查找消息文本,如下所示:

u.errors[:name].should include(I18n.t("activerecord.errors.models.user.attributes.name.too_short"))
Run Code Online (Sandbox Code Playgroud)

但是当我activerecord.errors.messages.too_short在locale文件中设置而不是特定于模型的消息时,这将失败.

那么,是否可以检查出现了哪种错误?

fny*_*fny 39

Rails 在2011年末和Rails v3.2中添加了一种查询ActiveModel错误的方法.只需检查是否有相应的错误#added?:

# An error added manually
record.errors.add :name, :blank
record.errors.added? :name, :blank # => true

# An error added after validation
record.email = 'taken@email.com'
record.valid? # => false
record.errors.added? :email, :taken # => true
Run Code Online (Sandbox Code Playgroud)

请注意,对于参数化的验证(例如:greater_than_or_equal_to),您还需要传递参数的值.

record.errors.add(:age, :greater_than_or_equal_to, count: 1)
record.errors.added?(:age, :greater_than_or_equal_to, count: 1)
Run Code Online (Sandbox Code Playgroud)

错误由他们的i18n密钥识别.您可以找到相应的密钥,以便在相应的Rails i18n文件中查找错误部分下的任何语言.

你可以问问其他一些漂亮的问题ActiveModel#Error#empty?#include?(attr),以及任何你可以问一个Enumerable.

  • 注意,您需要将选项作为某些检查的第三个参数传递.例如,`assert record.errors.added?(:slug,:too_short,count:5)`其中5是所需的长度. (3认同)

Jan*_*dek 12

I really don't like the idea of looking for translated error messages in Errors hash. After a conversation with a fellow Rubyists, I ended monkey patching Errors hash, so it saves the non-translated message first.

module ActiveModel
  class Errors
    def error_names
      @_error_names ||= { }
    end

    def add_with_save_names(attribute, message = nil, options = {})
      message ||= :invalid
      if message.is_a?(Proc)
        message = message.call
      end
      error_names[attribute] ||= []
      error_names[attribute] << message
      add_without_save_names(attribute, message, options)
    end

    alias_method_chain :add, :save_names
  end
end
Run Code Online (Sandbox Code Playgroud)

Then you can test like this:

u = User.new(:name => "a")
u.valid?
u.errors.error_names[:name].should include(:too_short)
Run Code Online (Sandbox Code Playgroud)

  • Rails应该完全做到这一点..如果它有验证类型,它们不会持久到错误对象的事实是奇怪和令人沮丧的. (3认同)
  • @MikeCampbell这个答案有点过时了.Rails v3.2通过`Error#added?`提供了这种能力.有关详细信息,请参阅[我的回答](http://stackoverflow.com/questions/4119379/how-to-test-which-validation-failed-in-activerecord/#16800379). (3认同)

Pet*_*own 5

我建议检查gem shoulda来处理这些类型的重复验证测试.它补充了RSpec或Test :: Unit,因此您可以编写简明的规范,例如:

describe User do
  it { should ensure_length_of(:name).is_at_least(2).is_at_most(5) }
end
Run Code Online (Sandbox Code Playgroud)


Ben*_*n5e 5

我使用的方法:

it "should not allow too short name" do
  u = User.new(:name => "a")
  expect{u.save!}.to raise_exception(/Name is too short/)
end
Run Code Online (Sandbox Code Playgroud)

我使用正则表达式来匹配异常消息,因为异常消息中可能有许多验证消息,但我们希望确保它包含与名称太短相关的特定代码段.

这种方法确实将您的断言与验证消息相结合,因此如果您每次修改验证消息,您可能也需要修改您的规范.总的来说,这是断言验证正在完成其工作的一种简单方法.