Rails come with some awesome assertion methods for writing tests:
assert_difference("User.count", +1) do create_a_user end
That asserts that the count of user was incremented by one. The plus sign is not needed, that’s just an integer, I add it to make things clear. You can mix several of this expressions into one assert_difference:
assert_difference(["User.count", "Profile.count"], +1) do create_a_user end
That works as expected, it asserts that both users and profiles were incremented by one. The problem I have is that I often found myself doing this:
assert_difference "User.count", +1 do assert_difference "Admin.count", 0 do assert_difference "Message.count", +3 do # We send three welcome messages to each user, like Gmail. create_a_user end end end
That looks ugly. Let’s try something different:
assert_difference("User.count" => +1, "Admin.count" => 0, "Message.count" => +3) do create_a_user end
Well, that looks nicer, and straightforward, so I implemented it (starting from Rails 3 assert_difference):
def assert_difference(expressions, difference = 1, message = nil, &block) b = block.send(:binding) if !expressions.is_a? Hash exps = Array.wrap(expressions) expressions = {} exps.each { |e| expressions[e] = difference } end before = {} expressions.each {|exp, _| before[exp] = eval(exp, b)} yield expressions.each do |exp, diff| error = "#{exp.inspect} didn't change by #{diff}" error = "#{message}.\n#{error}" if message assert_equal(before[exp] + diff, eval(exp, b), error) end end
Do you like it? If you do, let me know and I might turn this into a patch for Rails 3 (and then let them now, otherwise they’ll ignore it).
Update: this is now a gem.
Leave a Reply