Reputation: 1319
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
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