Support hash as first argument in `assert_difference`. (#31600)

* Support hash as first argument for `assert_difference`.

This allows to specify multiple numeric differences in the same assertion.
Example:

    assert_difference 'Article.count' => 1, 'Notification.count' => 2 do
      # post :create, params: { article: {...} }
    end

* Support error message when passing a hash as a first parameter

* Format CHANGELOG properly

[Julien Meichelbeck + Rafael Mendonça França]
This commit is contained in:
Julien Meichelbeck 2018-01-18 21:20:34 +01:00 committed by Rafael França
parent ccfc2d63ca
commit e0f0d717d6
3 changed files with 56 additions and 6 deletions

View File

@ -1,3 +1,10 @@
* Support hash as first argument in `assert_difference`. This allows to specify multiple
numeric differences in the same assertion.
assert_difference ->{ Article.count } => 1, ->{ Post.count } => 2
*Julien Meichelbeck*
* Add missing instrumentation for `read_multi` in `ActiveSupport::Cache::Store`.
*Ignatius Reza Lesmana*

View File

@ -58,6 +58,12 @@ module ActiveSupport
# post :create, params: { article: {...} }
# end
#
# A hash of expressions/numeric differences can also be passed in and evaluated.
#
# assert_difference ->{ Article.count } => 1, ->{ Notification.count } => 2 do
# post :create, params: { article: {...} }
# end
#
# A lambda or a list of lambdas can be passed in and evaluated:
#
# assert_difference ->{ Article.count }, 2 do
@ -73,20 +79,28 @@ module ActiveSupport
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
# post :delete, params: { id: ... }
# end
def assert_difference(expression, difference = 1, message = nil, &block)
expressions = Array(expression)
def assert_difference(expression, *args, &block)
expressions =
if expression.is_a?(Hash)
message = args[0]
expression
else
difference = args[0] || 1
message = args[1]
Hash[Array(expression).map { |e| [e, difference] }]
end
exps = expressions.map { |e|
exps = expressions.keys.map { |e|
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
}
before = exps.map(&:call)
retval = yield
expressions.zip(exps).each_with_index do |(code, e), i|
error = "#{code.inspect} didn't change by #{difference}"
expressions.zip(exps, before) do |(code, diff), exp, before_value|
error = "#{code.inspect} didn't change by #{diff}"
error = "#{message}.\n#{error}" if message
assert_equal(before[i] + difference, e.call, error)
assert_equal(before_value + diff, exp.call, error)
end
retval

View File

@ -115,6 +115,35 @@ class AssertDifferenceTest < ActiveSupport::TestCase
end
end
def test_hash_of_expressions
assert_difference "@object.num" => 1, "@object.num + 1" => 1 do
@object.increment
end
end
def test_hash_of_expressions_with_message
error = assert_raises Minitest::Assertion do
assert_difference({ "@object.num" => 0 }, "Object Changed") do
@object.increment
end
end
assert_equal "Object Changed.\n\"@object.num\" didn't change by 0.\nExpected: 0\n Actual: 1", error.message
end
def test_hash_of_lambda_expressions
assert_difference -> { @object.num } => 1, -> { @object.num + 1 } => 1 do
@object.increment
end
end
def test_hash_of_expressions_identify_failure
assert_raises(Minitest::Assertion) do
assert_difference "@object.num" => 1, "1 + 1" => 1 do
@object.increment
end
end
end
def test_assert_changes_pass
assert_changes "@object.num" do
@object.increment