Daikini

We're human after all.

Rails Validation Test Helpers

Posted by Jonathan on June 10, 2007 at 11:22 AM

I’ve written a couple test helpers that I’ve found to be pretty useful when testing ActiveRecord validations. Just add them to your RAILS_ROOT/test/test_helper.rb file.

The assert_errors_on method takes an object and a hash of attributes and the validation errors that they should have.

The assert_no_errors_on method takes an object and a list of attributes that should not have any validation errors.

   1  def assert_errors_on(object, errors = {})
   2    assert !object.valid?
   3    errors.each do |attribute, error| 
   4      assert_equal error.to_a.sort, object.errors.on(attribute).to_a.sort, "Error on #{attribute}" 
   5    end
   6  end
   7    
   8  def assert_no_errors_on(object, *attributes)
   9    object.valid?
  10    attributes.each { |attribute| assert_nil object.errors.on(attribute), "Error on #{attribute}" }
  11  end

You can then use them like this:

   1  require File.dirname(__FILE__) + '/../test_helper'
   2  
   3  class PersonTest < Test::Unit::TestCase
   4    def test_should_require_unique_email_address
   5      # Using assert_errors_on method
   6      person = Person.new :email_address => "cosmo@sprockets.com"
   7      assert_errors_on person, :email_address => "has already been taken"
   8  
   9      # Doing the same thing but without the assert_errors_on method
  10      person = Person.new :email_address => "cosmo@sprockets.com"
  11      assert !person.valid?
  12      assert_equal "has already been taken", person.errors.on(:email_address)
  13  
  14      # Using assert_no_errors on method
  15      person = Person.new :email_address => "john.smith@example.com"
  16      assert_no_errors_on person, :email_address
  17      
  18      # Doing the same thing but without the assert_no_errors_on method
  19      person = Person.new :email_address => "john.smith@example.com"
  20      person.valid?
  21      assert_nil person.errors.on(:email_address)
  22    end
  23  
  24    # We can test other attributes independently as well
  25  
  26    def test_should_require_a_first_name
  27      person = Person.new
  28      assert_errors_on person, :first_name => "can't be blank"
  29  
  30      person = Person.new :first_name => "Jonathan"
  31      assert_no_errors_on :person, :first_name
  32    end
  33  
  34    def test_should_require_a_last_name
  35      person = Person.new
  36      assert_errors_on person, :last_name => "can't be blank"
  37  
  38      person = Person.new :last_name => "Younger"
  39      assert_no_errors_on :person, :last_name
  40    end
  41  
  42    def test_should_blah_blah_blah
  43      ...
  44    end
  45  end

Updated 2007-11-28: Added additional attribute tests to show that the attributes can be tested in isolation without regards to ensuring that all of the attributes have to be valid for testing to work.

Updated 2007-09-14: Added code for showing what the tests would look like by not using the test helpers.

Tags: rails, testing
Hierarchy: previous, next

Comments

There are 3 comments on this post. Post yours →

Thanks for this simple yet useful helper methods! I have one question though: shouldn’t line 9 be “assert object.valid?”.

For the assert_no_errors_on method we are only checking if there are no errors on a particular attribute or set of attributes. The object may be valid but it is likely that it is not valid because other attributes that we are not checking may have errors.

If we did do “assert object.valid?” here we’d have to ensure that all attributes of the object were valid and not just the particular ones we are interested in testing at this time.

The reason why we wouldn’t want to ensure that all the attributes are valid here is for testing isolation. By being able to test each attribute independently of the others we can setup different tests for each attribute. I’ve updated the post to show an example of this.

Hope that helps and I’ve explained that well enough.

Thanks, Jonathan.

I found out that object.valid? validates the object. I was wondering why you included it, if you weren’t checking its value, but now I know that else object.validate wouldn’t be run.

Post a comment

Required fields in bold.