RSpec test custom validator

17,486

Solution 1

The problem is that you're expecting @time_event.errors to behave like an array of strings. It doesn't, it returns ActiveModel::Errors. As others pointed out, you also need to trigger the validations with a call to 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

Solution 2

This solution works for me (using Mongoid):

The model

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

The test:

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

So in your case I'd write a test like this (if using 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

Solution 3

There are no errors because you haven't called an event that triggers the errors. This happens normally when a record is created or saved. You may not want to hit the database in your test though and then you can use the method valid? like this:

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

Me personally would put these two tests into one since valid? is called in the first case.

Also a minor: if record.end_time is better than if !record.end_time.nil?. (In my opinion at least.... :-) )

Share:
17,486
siekfried
Author by

siekfried

Updated on June 09, 2022

Comments

  • siekfried
    siekfried almost 2 years

    I have the following validator in my model:

    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
    

    How can I test it using Rspec?

    Here is what I have tried so far: (thanks to 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
    

    But I get the following errors:

    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={}>
    

    What am I doing wrong? And how can I achieve my goal? Any help or suggestion would be appreciated. Thanks.

  • siekfried
    siekfried about 11 years
    You're right I need to add the full_messages to get the error. However like the other said, I need to actually test the validation with valid?
  • siekfried
    siekfried about 11 years
    You are right, but like @Benjamin Sullivan suggested, I also need to add the full_messages to the errors so that my test pass. Thanks also for the minor :)