Last month I wrote about the new assert_difference
test helper and how to use it to test any kind of difference using lambdas. Since then there have been a couple changes to assert_difference
that make it easier for doing this sort of thing. Yes, this is life on the edge. Things change, sometimes faster than a humbler blogger can keep up with.
The first big change was that instead of an object and a method, assert_difference
now takes a string that gets eval'ed. The second change is that it doesn't just take one string, it takes an array of strings that get eval'ed. And it's important to note that the strings are eval'ed in the binding scope of assert_difference
block, so they will have access to variables that are set in the block.
Here's my previous example:
def test_create_user
login = "bob"
name = "Bob Dobbs"
assert_difference( lambda { User.find_all_by_login(login).size }, :call, 1 ) do
bob = User.create!(:login => login, :name => name)
assert_equal login, bob.login
assert_equal name, bob.name
end
end
Here's how to do that in the new way:
def test_create_user
login = "bob"
name = "Bob Dobbs"
assert_difference( 'User.find_all_by_login(login).size', 1 ) do
bob = User.create!(:login => login, :name => name)
assert_equal login, bob.login
assert_equal name, bob.name
end
end
And if you want to make sure the user's mailbox is created at the same time:
def test_create_user
login = "bob"
name = "Bob Dobbs"
assert_difference( ['User.find_all_by_login(login).size', 'Mailbox.count'], 1 ) do
bob = User.create!(:login => login, :name => name)
assert_equal login, bob.login
assert_equal name, bob.name
assert_not_nil bob.mailbox
end
end
The difference defaults to 1 so you don't really need to specify it, but I like seeing it there all explicit and obvious. YMMV.
I find this change interesting. On the one hand, it makes everyone who was using the old style helper change their code. This helper was floating around for a long time before getting included into core, so a lot of people are going to have to update. What a pain. On the other hand, I like the extra flexibility. My preference is to use a lambda instead of an eval'ed string, but I can see how the lambda syntax is a bit unwieldy for this use. I've seen some of the ideas for a more compact syntax for lambdas in a future Ruby, and this is a great example of a place where that would be useful. I think if we had a two-character lambda notation, we'd be seeing lambda's here instead of eval'ed strings. (Pretend I just inserted a clever comparison with Smalltalk blocks and why they are so easy to use. If you need it for real, feel free to make it up yourself.)