CheeseFry
CheeseFry

Reputation: 1319

Custom Validator Test - Contains Errors?

Wrote a custom validator in my rails project and want to write a test for when nil is passed into the record.

The validator code

class FutureValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value == nil
      record.errors[attribute] << "can't be nil"
    elsif value <= Time.now
      record.errors[attribute] << (options[:message] || "can't be in the past!")
    end
  end
end

Possible test

test "future validator rejects nil values" do
  @obj.future = nil
  assert_includes @obj.errors
end

I'd like to not only write a test that checks that assert_not @obj.valid? but actually demonstrate that the error message is being passed back. If that's asking too much I'll settle for knowing that an error message is coming back but currently my test isn't working.

It is returning

ArgumentError: wrong number of arguments (1 for 2..3)

Update Getting close to a green test

  test "future validator rejects nil values" do
    @obj.future = nil
    @obj.valid?
    assert_includes @obj.errors.messages, {future: ["can't be nil"]}
  end

Is returning

FutureValidatorTest#test_future_validator_rejects_nil_values [/Users/rwdonnard/WorkSpace/charter-pay-api/test/models/future_validator_test.rb:42]: Expected {:future=>["can't be nil"]} to include {:future=>["can't be nil"]}.

Upvotes: 2

Views: 301

Answers (1)

Ioannis Tziligkakis
Ioannis Tziligkakis

Reputation: 711

The issue seems to be the way you test your class and most probably the exception gets raised from assert_includes. assert_includes expects at least 2 arguments with the first being a collection and the second being the object you expect to be included, in your case an array of errors and the error you expect respectively. Also your test is going to fail if you don't populate the @obj.errors collection, something that requires to call @obj.valid?

Your test should look like this:

test "future validator rejects nil values" do
  @obj.future = nil
  @obj.valid?
  assert_includes @obj.errors.messages, { future: ["can't be nil"] }
end

This way you make sure that your model is invalid if future.nil? regardless of other validations in place.


I'd also suggest that you don't check for presence in your custom validator since Rails already provide a way for this. You could have your validator look like this:

class FutureValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value <= Time.now
      record.errors[attribute] << (options[:message] || "can't be in the past!")
    end
  end
end

and setup validation in your model like this:

class SomeClass < ActiveRecord::Base
  validates :future, presence: true, future: true
end

You could also simplify your tests like:

test "is invalid with nil future date" do
  @obj.future = nil
  @obj.valid?
  assert_includes @obj.errors.messages[:future], "can't be nil"
end

test "future validator rejects past dates" do
  @obj.future = Date.new(1987, 1, 1)
  @obj.valid?
  assert_includes @obj.errors.messages[:future], "can't be in the past!"
end

Upvotes: 3

Related Questions