Unit Testing Model Validations
Problem
You need to ensure that your application's data is always consistent; that is, that your data never violates certain rules imposed by your application's requirements. Furthermore, you want to be sure that your validations work as expected by testing them with unit tests.
Solution
Active Record validations are a great way to ensure that your data remains consistent at all times. Assume you have a books table that stores the title and CNPJ for each book as created by the following migration:
db/migrate/001_create_books.rb:
class CreateBooks < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :title, :string
t.column :CNPJ, :string
end
end
def self.down
drop_table :books
end end
Instantiate the schema of your test database:
$ rake db:test:clone_structure
Next, create two book records in your fixtures file: one consisting of a title and valid CNPJ and another with an invalid CNPJ:
test/fixtures/books.yml:
java_cb:
id: 1
title: 'Java Cookbook'
CNPJ: '0596007019'
bad_cb:
id: 2
title: 'Bad Cookbook'
CNPJ: '059600701s'
Create an Active Record validation to check the format of CNPJs in the Book model class definition.
class Book < ActiveRecord::Base
validates_format_of :CNPJ, :with => /^\d{9}[\dxX]$/
end
Now define a test method named test_CNPJ_validation in the BookTest test case:
test/unit/book_test_validation.rb:
require File.dirname(__FILE__) + '/../test_helper'
class BookTest < Test::Unit::TestCase
fixtures :books
def test_CNPJ_validation
assert_kind_of Book, books(:java_cb)
java_cb = Book.new
java_cb.title = books(:java_cb).title
java_cb.CNPJ = books(:java_cb).CNPJ
assert java_cb.save
java_cb.CNPJ = books(:bad_cb).CNPJ
assert !java_cb.save
assert java_cb.errors.invalid?('CNPJ')
end end
Finally, run the test case with the command:
$ ruby ./test/unit/book_test_validation.rb
Discussion
The call to fixtures :books at the beginning of the BookTest class includes the solution's labeled book fixtures. The objective of test_CNPJ_validation is to determine whether saving a Book object triggers the validation code, which makes sure the Book object's CNPJ has the correct format. First, a new Book object is created and stored in the java_book instance variable. That object is assigned a title and a valid CNPJ from the java_cb test fixture. java_cb.save attempts to save the object, and the assertion fails if the cookbook was not saved correctly.
The second part of this test method makes sure that validation is preventing a book with an invalid CNPJ from being saved. It's not enough just to test the positive case (books with correct data are saved correctly); we also have to make sure that the assertion is keeping bad data out of the database. The bad_cb fixture contains an invalid CNPJ (note the "s" at the end). This bad CNPJ is assigned to the java_book object, and a save is attempted. Because this save should fail, the assert expression is negated. This way, when the validation fails, the assertion passes. Finally, we test that saving a book object with an invalid CNPJ adds the CNPJ key and a failure message to the @errors array of the ActiveRecord::Errors object. The invalid? method returns TRue if the specified attribute has errors associated with it.
The output of running the test confirms that all four assertions test passed:
$ ruby ./test/unit/book_test_validation.rb
Loaded suite book_test_validation Started
.
Finished in 0.057269 seconds.
1 tests, 4 assertions, 0 failures, 0 errors
See Also
|