When a test case calls methods that write new records to a
database, sometimes the test needs to fetch those records back and
inspect them. This post develops assert_latest, an
assertion that detects newly created records.
The following methods use Ruby on Rails, and its exquisite persistence layer, ActiveRecord. The techniques apply to developer tests on any platform.
We start with a trivial method that accepts an Array of
prop names, and creates a prop record
for each one:
def create_props(props) props.each{|u| Prop.create!(:name => u) } end
And here’s an incomplete test case that doesn’t know if the method worked:
def test_might_create_props create_props(%w[hip hop dont stop]) end
When we design code for testing, our code should not have too many extra lines. If only test cases need a line of code, we should work to take it out.
Sometimes Ruby makes design-for-testing very easy. If we change
.each to .map, our tests get more bang
for very little buck, and our fictitious method gets a better interface:
def return_created_props(props) return props.map{|u| Prop.create!(:name => u) } end def test_return_created_props names = %w[hip hop dont stop] props = return_created_props(names) assert_equal names, props.map(&:name) # more assertions here end
Sometimes code that creates new database records grows
more complex than our simple example. Specifically, when
a Rails test case simulates a Controller responding to a
web page, any new data records might not come back in the
assigns() system.
We need an assertion that detects new items, to write our test like this:
def test_find_created_props names = %w[hip hop dont stop] props = assert_latest Prop do create_props(names) end assert_equal names, props.map(&:name) end
That decouples the test from the tested code. Here’s how it works.
assert_latest calls get_latest,
which inspects the
record’s maximum id key before its block,
and returns all the records created after the block.
def assert_latest(model, message = nil, &block) return get_latest(model, &block) || flunk_latest(model, message, true) end
If the method returns nil,
we flunk the assertion.
Every assert_ needs a
deny_; we just reverse the polarity:
def deny_latest(model, message = nil, &block) get_latest(model, &block) and flunk_latest(model, message, false) end
That fails when your production code accidentally creates new records that it should not.
get_latest fetches the maximum id,
if any. Then it calls your block, allowing it
to attempt to write records.
def get_latest(model, &block) max_id = model.maximum(:id) || 0 block.call all = model.find( :all, :conditions => "id > #{max_id}", :order => "id asc" ) return *all # <-- returns nil for [], # one object for [x], # or an array with more than one item end
After your block returns, we find all records
with an id greater than our “high water mark”. This technique
depends on databases that increment their id keys monotonically
and eternally; if your database is configured to perform some other way,
you’ll need a better technique.
The little star * converts the array into
a kind of “inline expression”, as if all = [1,2,3]
were instead all = 1,2,3. That trick permits us
to return nil, one new record, or an array of records.
And, finally, any assertion failure should provide complete diagnostics, to help rapidly determine how to revert or debug the code:
def flunk_latest(model, message, polarity) flunk build_message( message, "new #{model.name.pluralize} should " + "#{'not' unless polarity} be created" ) end
The best persistence layers operate as records in databases
when you want them to, and as objects in memory when you
don’t. assert_latest decouples your tests
from your production code, using
ActiveRecord’s
powerful techniques that decouple your objects from
your database.

Hi Phlip,
Perhaps you can consider posting on O'Reilly Ruby? We can use some more content there, and some folks are definitely interested in Rails stuff over there.