November 16, 2009
1:45 pm EST
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.