Wed, 21 Jun 06

More helpful messages for Active Record validation errors in Rails testing

In testing (and production code) we always try to persist Active Record models with the bang variants (save! and create!). This allows us to fail fast if any of our models fail our validation rules.

The problem is that the standard test/unit reporting framework displays the exception without any details of why the validation failed.

 1) Error:​
test_should_fail_validation(AardvarkTest):​
ActiveRecord::RecordInvalid:​ActiveRecord::RecordInvalid
   /opt/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/validations.rb:711:in 'save!'
   /opt/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/validations.rb:673:in 'create!'
   /Users/chrisroos/dev/example_rails/test/unit/aardvark_test.rb:22:in 'test_should_fail_validation'

1 tests, 0 assertions, 0 failures, 1 errors

Note. This isn’t the fault of test/unit. It has to deal with generic exceptions and so cannot ‘know’ that Active Record Exceptions can be used to find the reason for the failure.

At this point, it would be very easy to jump into the code of the Active Record model, check out the validations and satisfy them within your code. However, it’d be much cooler if we could just add the reason for the failures to the error message that is displayed.

I initially played around with a method that took a block and would wrap the block with a begin/rescue clause to explicitly catch RecordInvalid exceptions, displaying the reason for failure.

Then I thought it’d be cool if we could integrate slightly more with both test/unit and rails. The result is a plugin (source) that can be installed as follows.

$ script/plugin install http://chrisroos.googlecode.com/svn/trunk/plugins/test_unit_active_record_errors/

The output from a failed validation now includes the validation errors. Yay.

 1) Error:​
test_should_fail_validation(AardvarkTest):​
ActiveRecord::RecordInvalid: ActiveRecord::RecordInvalid
   /opt/local/lib//gems/1.8/gems/activerecord-1.13.2/lib/active_record/validations.rb:711:in `save!'
   /opt/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/validations.rb:673:in `create!'
   /Users/chrisroos/dev/example_rails/test/unit/aardvark_test.rb:22:in `__test_should_fail_validation_without_custom_error_handling'
   /Users/chrisroos/dev/example_rails/config/../vendor/plugins/test_unit_active_record_errors/lib/test_unit_active_record_errors.rb:29:in `__send__'
   /Users/chrisroos/dev/example_rails/config/../vendor/plugins/test_unit_active_record_errors/lib/test_unit_active_record_errors.rb:29:in `test_should_fail_validation'
   /Users/chrisroos/dev/example_rails/config/../vendor/plugins/test_unit_active_record_errors/lib/test_unit_active_record_errors.rb:27:in `test_should_fail_validation'
ActiveRecord Errors:​Surname can't be blank, Email can't be blank

1 tests, 0 assertions, 0 failures, 1 errors

The code is pretty hacky and includes no tests. It’s only been tested on Mac Os X 10.4.6 with Ruby 1.8.2 and Rails 1.0.0 but please feel free to do what you will with it if you think it might be of some use to you.