Ruby on Rails: Test Model Domain Changes
You know how tests are fundamental for a well-developed project, for this reason we should create step-by-step a net of test cases. Of course we also should be able to change rapidly our tests as the same we do with our code. Ruby on Rails is a great framework, because its shortcuts, the wide usage of DSL etc.. All this stuff can save a lot of time, but what about tests? Are we really able to follow our code?
Create and Destroy
ActiveSupport provides few useful tools to improve our test, I really appreciate assert_difference and assert_no_difference.
Basically, this two methods accepts as arguments a code chunk (as string) and a block. When the test run, it binds the block first, then it assert if the changements caused by the block call are the same expected by first argument.
def test_should_be_created
assert_difference 'Person.count' do
create_person
end
end
We are testing a Person creation, we pass as first argument 'Person.count', and the code that should correctly save the person. If the model will be saved, a new record should exists into the database table. At this moment assert_difference evaluates the first argument, and assert if there are differences in the Person count.
def test_should_be_destroyed
assert_difference 'Person.count', -1 do
destroy_person
end
end
This example is just a bit different, we are also passing a Fixnum as argument. This because we want assert another difference from the default one, which is +1. So, if the model will be correctly saved, we will have a negative difference, of one, into the Person count.
def test_should_require_first_name_on_create
assert_no_difference 'Person.count' do
create_person
assert person.errors.on(:first_name)
end
end
The third example uses assert_no_difference, to test aganist model validations. ActiveRecord, by default, prevents the creation of a model if a validation doesn’t pass. In this case our model requires first_name as mandatory attribute, but unfortunately it’s nil, so the creation fails and the brand new record will be not created.
Update
As you can see, those two methods are very useful for test creation and destruction of models, but totally missing the goal of the update. In fact, the update process of a record, doesn’t produces numerical differences.
I created two methods to supply this lack.
def assert_updated(model, message = nil, &block)
yield
assert_not_equal model.attributes, model.reload.attributes, message
end
def assert_not_updated(model, message = nil, &block)
yield
assert_equal model.attributes, model.reload.attributes, message
end
Just add them to your test/test_helper.rb, and they will be available in all your test cases.
def test_should_update
assert_updated person do
update_person
end
end
First, you should notice that the first argument it isn’t a string but an ActiveRecord. The behaviour of this method is similar to the previous I illustrated, it first bind the block, then assert if the attributes of the model are different.
It internally uses ActiveRecord::Base#attributes which returns an hash of model attributes, then assert the differences with Ruby’s assert_not_equal.
def test_should_require_first_name_on_update
assert_not_updated person do
update_person
assert person.errors.on(:first_name)
end
end
Similarly to all other examples, it first performs the block, call then assert there are no changes in the model attributes.
Conclusion
Those methods should provide a rapid way to write and mantain your test cases.
If you enjoyed this post, feel free to recommend me on Working With Rails.