RSpec测试自定义验证器

7

我的模型中有以下验证器:

class ContinuumValidator < ActiveModel::Validator
  def validate(record)
    if !record.end_time.nil? and record.end_time < record.start_time
      record.errors[:base] << "An event can not be finished if it did not start yet..."
    end
  end
end

class Hrm::TimeEvent < ActiveRecord::Base
  validates_with ContinuumValidator
end

如何使用Rspec进行测试?

以下是我尝试过的方法:(感谢zetetic

describe "validation error" do
  before do
    @time_event = Hrm::TimeEvent.new(start_time: "2012-10-05 10:00:00", end_time: "2012-10-05 09:00:00", event_type: 2)
  end

  it "should not be valid if end time is lower than start time" do
    @time_event.should_not be_valid
  end

  it "raises an error if end time is lower than start time" do
    @time_event.errors.should include("An event can not be finished if it did not start yet...")
  end
end

但我遇到了以下错误:
1) Hrm::TimeEvent validation error raises an error if end time is lower than start time
   Failure/Error: @time_event.errors.should include("An event can not be finished if it did not start yet...")

   expected #<ActiveModel::Errors:0x007fd1d8e02c50 @base=#<Hrm::TimeEvent id: nil, start_time: "2012-10-05 08:00:00", end_time: "2012-10-05 07:00:00", event_type: 2, employee_id: nil, created_at: nil, updated_at: nil, not_punched: false, validated: false, replace_id: nil>, @messages={}> to include "An event can not be finished if it did not start yet..."

   Diff:
   @@ -1,2 +1,5 @@
   -["An event can not be finished if it did not start yet..."]
   +#<ActiveModel::Errors:0x007fd1d8e02c50
   + @base=
   +  #<Hrm::TimeEvent id: nil, start_time: "2012-10-05 08:00:00", end_time: "2012-10-05 07:00:00", event_type: 2, employee_id: nil, created_at: nil, updated_at: nil, not_punched: false, validated: false, replace_id: nil>,
   + @messages={}>

我做错了什么?我该如何达成我的目标?任何帮助或建议都将不胜感激。谢谢。
5个回答

14
问题在于你期望@time_event.errors的行为像一个字符串数组。但它不是,它返回ActiveModel::Errors。正如其他人指出的那样,你还需要通过调用valid?来触发验证:
it "raises an error if end time is lower than start time" do
  @time_event.valid?
  @time_event.errors.full_messages.should include("An event can not be finished if it did not start yet...")
end

你说得对,我需要添加full_messages来获取错误信息。但是像其他人所说的那样,我需要使用valid?实际测试验证。 - siekfried

3
这个解决方案适用于我(使用Mongoid): 模型
class OpLog
...
field :from_status, type: String
field :to_status,   type: String
...
validate :states_must_differ

def states_must_differ
  if self.from_status == self.to_status
    errors.add(:from_status, "must differ from 'to_status'")
    errors.add(:to_status, "must differ from 'from_status'")
  end
end
...
end

测试内容:

it 'is expected to have different states' do
  expect { create(:oplog, from_status: 'created', to_status: 'created').to raise_error(Mongoid::Errors::Validations) }
end

如果您正在使用ActiveRecord,那么在您的情况下,我会编写以下测试:

it 'raises an error if end time is lower than start time' do
  expect { create(Hrm::TimeEvent.new(start_time: "2012-10-05 10:00:00", end_time: "2012-10-05 09:00:00", event_type: 2)) }.to raise_error(ActiveRecord::Errors)
end

2

没有错误是因为您还没有调用触发错误的事件。这通常在创建或保存记录时发生。但是,在测试中您可能不想访问数据库,那么您可以使用以下方法:valid?

it "raises an error if end time is lower than start time" do
  @time_event.valid?
  @time_event.errors.should include("An event can not be finished if it did not start yet...")
end

我个人会将这两个测试合并,因为在第一个测试中调用了valid?方法。

另外,小建议:使用if record.end_time比使用if !record.end_time.nil?更好。(至少在我看来是这样的.... :-) )


你是正确的,但就像@Benjamin Sullivan建议的那样,我还需要将full_messages添加到错误中以使我的测试通过。也谢谢您指出的小问题 :) - siekfried

0

我认为记录没有通过验证,因此验证器没有运行,也没有出现错误。您可以在代码输出中看到这一点。"validated: false"

尝试:

it "raises an error if end time is lower than start time" do
  @time_event.valid?
  @time_event.errors.should include("An event can not be finished if it did not start yet...")
end

0

你实际上还没有测试验证,我建议你制作一个单一的规范。

describe "validation error" do
  before { @time_event = Hrm::TimeEvent.new(start_time: "2012-10-05 10:00:00", end_time: "2012-10-05 09:00:00", event_type: 2) }

  it "raises an error if end time is lower than start time" do
    @time_event.valid?
    @time_event.errors.should include("An event can not be finished if it did not start yet...")
  end
end

class ContinuumValidator < ActiveModel::Validator
  def validate(record)
    if record.end_time and record.end_time < record.start_time
      record.error.add_to_base << "An event can not be finished if it did not start yet..."
    end
  end
end

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