Sometimes a test case must detect when production code
creates new records, as a side-effect. assert_latest{}
detects all new records of a given type, and returns them like this:
f1, f2 = assert_latest Foo do 2.times{ Foo.create } end assert 'items return ordered by id' do f1.id > 0 and f2.id > f1.id end
This post shows how to use assert_latest{} in more
advanced configurations. It can detect records of more than one type, and
can detect records that belong to only one association. Our platform is
Ruby on Rails, yet - as usual -
the lessons apply to any unit tests.
Our tool list includes the usual suspects:
assert_latest, from theassert_efficient_sqlgem- Ruby on Rails (with ActiveRecord)
assert{ 2.0 }
The assert_latest system uses
assert_efficient_sql - review its blog entry
here.
Get the latest version with gem install
assert_efficient_sql.
Plurality
Start by adding this to one of your test suites or specifications:
require 'assert_efficient_sql'
A real project would naturally write tests that call production code,
possibly with nested and hidden creation lines. To illustrate
assert_latest{} in action, we will first sample from its
own unit tests. These call no production code; they simply concoct two
models, Foo and T2, and then experiment by
constructing them.
This test shows how assert_latest can return two types at once:
t, fz = assert_latest T2, Foo do T2.create 2.times{ Foo.create } end assert do t.id > 0 and fz[0].id > 0 and fz[1].id > fz[0].id end
The assertion returned an array containing the one first item,
and a nested array with the two second items. Essentially
[ t, [ fz1, fz2 ] ].
If the system found only one Foo, it would have returned
[ t, fz1 ], without the nested array.
The following assertions will implicitly test the
new records’ plurality simply by using them. If production
code only created one Foo, then fz[0] would fail.
(Also note that real tests would assert something more useful
than a populated .id field!)
Fault Diagnostics
To accelerate development, assertion diagostics should take care
to declare what went wrong. This test case shows assert_latest
failing to create T2 records:
assert_flunked /diagnostic.*new T2 record/m do f, t = assert_latest Foo, T2, 'diagnostic' do Foo.create end end
The assert_flunked detects the assertion
raised an error containing your custom ‘diagnostic’
text, and the verbiage “should create new T2 record(s)”. assert_latest{}
also decorates its faults with the reflected source of its block, using the
RubyReflector system from assert{ 2.0 }.
Denial
Most generic assertions should have a matching denial. This test trivially reveals the contrapositive of the last test:
assert_flunked /diagnostic.*new Foo record/m do f, t = deny_latest Foo, T2, 'diagnostic' do Foo.create end end
Note the diagnostic now complains that your production code
should not have created a new Foo record.
Who’s your Daddy?
assert_latest’s arguments do not need to be raw models. They
can be anything that responds to .maximum and
.find. So the more you tell
assert_latest, the more accurate results.
This example works with rboard, a blog by Radar:
user = users(:plebian) topic = topics(:user_2) p1, p2 = assert_latest user.posts, topic.posts do user.posts.create :topic => topic, :text => 'Vote McBush, early and often!' end assert{ p1.id == p2.id }
The test uses two associations, so it cross-tests both parents of the
new Post. A new record with a different user and
topic cannot accidentally pass the test.
Polygamous and explicitly-associated assert_latest{} blocks work great
in integration tests and end-to-end tests. Short test cases must call a whole
lot of code, and then assert that all of it created only the correct
output records. assert_latest{} lets you list exactly what
you expect, without redundant lines in your test case, just to look
up and inspect the created records.
