Unit testing ActiveRecord validations and PostgreSQL constraints
September 16, 2010
Some of the more rudimentary aspects of unit testing involve validation. These tests may exercise ActiveRecord or the database constraints created by a migration.
In a chapter from Enterprise Rails, 'Database As a Fortress', author Dan Chak advocates for employing the data integrity features of modern databases, particularly PostgreSQL. With Realized-app, we use PostgreSQL as a persistent data store.
To go beyond just testing the ActiveRecord model validations, in testing a database constraint Dan suggests using the following test_helper.rb method:
def expect_db_error(&block)
begin
yield
rescue ActiveRecord::StatementInvalid
database_threw_error = true
rescue
something_else_threw_error = true
end
assert !something_else_threw_error, 'defective case'
assert database_threw_error && !something_else_threw_error
end
One difficulty with testing database constraints is that the ActiveRecord model validations may catch some errors before they reach the PostgreSQL database. As a work-around, we can add another method:
def svf(obj) obj.save_with_validation(false) end
Using these test helpers, this test will properly exercise the database constraints defined by a migration.
test 'name too long' do
expect_db_error { svf(some_model_save(:name => 'name_too_long')) }
end
Such validation tests tend to be similar among different models. Just as concerned_with allowed for division of model logic into discrete files, we place unit test validation tests in a separate /test/unit/validation directory. This prevents cluttering model-specific logic with boilerplate validation checks.