Tag: assert_difference

  • 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.