Disposable tables for Rails tests
It turns out that it’s really easy to create tables and models dynamically within a Rails unit test. It’s a useful technique for reducing dependencies when testing.
To explain why you’d want to do this, imagine a hypothetical
acts_as_melon_farmer module that extends some models
in your Rails application with additional behaviour:
class Foo < ActiveRecord::Base acts_as_melon_farmer end class Bar < ActiveRecord::Base acts_as_melon_farmer end
What acting as a melon farmer really means is beyond the scope of this exercise.
The operation of the module depends on the ActiveRecord classes which it extends: in order to test the module, you need to provide that basic functionality.
One way to do this is to use an existing concrete class
Bar in the example). This works,
but the tests are messy, poorly delineated, and brittle.
A better solution, perhaps, is to use mocking and stubbing to handle the interface between the module and ActiveRecord. However, one thing that’s very difficult to mock is the database—especially in ActiveRecord, where the SQL is exposed at a high level. If you want to test the actual behaviour of a particular query, you need to put it through the database layer. That means that you need a model definition and an associated database table.
As this model has no meaning outside the test, wouldn’t it be nice if you could create it in an entirely self-contained manner? Well, you can:
class ActsAsMelonFarmerTest < Test::Unit::TestCase class Migration < ActiveRecord::Migration def self.up create_table 'melon_farmer_test_objects', :force => true do |t| # column definitions end end def self.down drop_table 'melon_farmer_test_objects' end end class MelonFarmerTestObject < ActiveRecord::Base acts_as_melon_farmer end def setup Migration.up end def teardown Migration.down end # Your tests go here end
I love it when there’s a clean solution to a problem.
I also need to pass on credit to Chris for suggesting that database migrations might work, and Ben, with whom I worked on this particular piece of code.