ruby-on-railsrubyrspecrspec-railsshoulda-matchers

Why are tests failing due to validation?


I am writing tests for an application and ran into a problem.

I have the next validation im my model:

  def correct_date
    errors.add(:occured_at, 'must be in past or present time') if occured_at > 5.minutes.from_now
  end

And when I run even the simplest tests on the model, they fail.

  describe 'associations' do
    it { should belong_to(:category).optional }
  end

I get an error:

     Failure/Error: errors.add(:occured_at, 'must be in past or present time') if occured_at > 5.minutes.from_now
     
     NoMethodError:
       undefined method `>' for nil:NilClass

Solution

  • By default, that test will create a new object with a simple Model.new call (where Model is the model class being tested). In your case, Model.new doesn't produce a valid object so you need to tell Rspec what subject to use for those tests:

    describe 'associations' do
      subject { FactoryBot.create(:whatever_the_factory_is_called) }
    
      it { should belong_to(:category).optional }
    end
    

    You should also fix your correct_date validation method to handle occurred_at.nil?; either validate that it isn't nil in correct_date:

    def correct_date
      if(occurred_at.nil?)
        # complain
      elsif(occurred_at > 5.minutes.from_now)
        errors.add(:occured_at, 'must be in past or present time')
      end
    end
    

    or validate the presence separately and skip the > when occurred_at.nil?:

    validates :occurred_at, presence: true
    
    def correct_date
      errors.add(:occured_at, 'must be in past or present time') if occurred_at && occured_at > 5.minutes.from_now
    end
    

    Making the occurred_at column in the database not null would also be advisable.

    If occurred_at is truly optional, then you'd only need to update correct_date to account for occurred_at.nil?:

    def correct_date
      errors.add(:occured_at, 'must be in past or present time') if occurred_at && occured_at > 5.minutes.from_now
    end