aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG.md50
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb89
-rw-r--r--activesupport/test/test_case_test.rb106
3 files changed, 236 insertions, 9 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 6103857a41..3749dda9fc 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,36 @@
+* Introduce `assert_changes` and `assert_no_changes`.
+
+ `assert_changes` is a more general `assert_difference` that works with any
+ value.
+
+ assert_changes 'Error.current', from: nil, to: 'ERR' do
+ expected_bad_operation
+ end
+
+ Can be called with strings, to be evaluated in the binding (context) of
+ the block given to the assertion, or a lambda.
+
+ assert_changes -> { Error.current }, from: nil, to: 'ERR' do
+ expected_bad_operation
+ end
+
+ The `from` and `to` arguments are compared with the case operator (`===`).
+
+ assert_changes 'Error.current', from: nil, to: Error do
+ expected_bad_operation
+ end
+
+ This is pretty useful, if you need to loosely compare a value. For example,
+ you need to test a token has been generated and it has that many random
+ characters.
+
+ user = User.start_registration
+ assert_changes 'user.token', to: /\w{32}/ do
+ user.finish_registration
+ end
+
+ *Genadi Samokovarov*
+
* Add `:fallback_string` option to `Array#to_sentence`. If an empty array
calls the function and a fallback string option is set then it returns the
fallback string other than an empty string.
@@ -15,14 +48,14 @@
* `travel/travel_to` travel time helpers, now raise on nested calls,
as this can lead to confusing time stubbing.
-
+
Instead of:
-
+
travel_to 2.days.from_now do
# 2 days from today
travel_to 3.days.from_now do
# 5 days from today
- end
+ end
end
preferred way to achieve above is:
@@ -30,13 +63,12 @@
travel 2.days do
# 2 days from today
end
-
- travel 5.days do
- # 5 days from today
- end
-
+
+ travel 5.days do
+ # 5 days from today
+ end
+
*Vipul A M*
-
* Support parsing JSON time in ISO8601 local time strings in
`ActiveSupport::JSON.decode` when `parse_json_times` is enabled.
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index ad83638572..7770aa8006 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -1,6 +1,8 @@
module ActiveSupport
module Testing
module Assertions
+ UNTRACKED = Object.new # :nodoc:
+
# Asserts that an expression is not truthy. Passes if <tt>object</tt> is
# +nil+ or +false+. "Truthy" means "considered true in a conditional"
# like <tt>if foo</tt>.
@@ -92,6 +94,93 @@ module ActiveSupport
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
+
+ # Assertion that the result of evaluating an expression is changed before
+ # and after invoking the passed in block.
+ #
+ # assert_changes 'Status.all_good?' do
+ # post :create, params: { status: { ok: false } }
+ # end
+ #
+ # You can pass the block as a string to be evaluated in the context of
+ # the block. A lambda can be passed for the block as well.
+ #
+ # assert_changes -> { Status.all_good? } do
+ # post :create, params: { status: { ok: false } }
+ # end
+ #
+ # The assertion is useful to test side effects. The passed block can be
+ # anything that can be converted to string with #to_s.
+ #
+ # assert_changes :@object do
+ # @object = 42
+ # end
+ #
+ # The keyword arguments :from and :to can be given to specify the
+ # expected initial value and the expected value after the block was
+ # executed.
+ #
+ # assert_changes :@object, from: nil, to: :foo do
+ # @object = :foo
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do
+ # post :create, params: { status: { incident: true } }
+ # end
+ def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block)
+ exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
+
+ before = exp.call
+ retval = yield
+
+ unless from == UNTRACKED
+ error = "#{expression.inspect} isn't #{from}"
+ error = "#{message}.\n#{error}" if message
+ assert from === before, error
+ end
+
+ after = exp.call
+
+ if to == UNTRACKED
+ error = "#{expression.inspect} didn't changed"
+ error = "#{message}.\n#{error}" if message
+ assert_not_equal before, after, error
+ else
+ message = "#{expression.inspect} didn't change to #{to}"
+ error = "#{message}.\n#{error}" if message
+ assert to === after, error
+ end
+
+ retval
+ end
+
+ # Assertion that the result of evaluating an expression is changed before
+ # and after invoking the passed in block.
+ #
+ # assert_no_changes 'Status.all_good?' do
+ # post :create, params: { status: { ok: true } }
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
+ # post :create, params: { status: { ok: false } }
+ # end
+ def assert_no_changes(expression, message = nil, &block)
+ exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
+
+ before = exp.call
+ retval = yield
+ after = exp.call
+
+ error = "#{expression.inspect} did change to #{after}"
+ error = "#{message}.\n#{error}" if message
+ assert_equal before, after, error
+
+ retval
+ end
end
end
end
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 18228a2ac5..772c3cfca7 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -111,6 +111,112 @@ class AssertDifferenceTest < ActiveSupport::TestCase
end
end
end
+
+ def test_assert_changes_pass
+ assert_changes '@object.num' do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_pass_with_lambda
+ assert_changes -> { @object.num } do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_from_option
+ assert_changes '@object.num', from: 0 do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_from_option_with_wrong_value
+ assert_raises Minitest::Assertion do
+ assert_changes '@object.num', from: -1 do
+ @object.increment
+ end
+ end
+ end
+
+ def test_assert_changes_with_to_option
+ assert_changes '@object.num', to: 1 do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_wrong_to_option
+ assert_raises Minitest::Assertion do
+ assert_changes '@object.num', to: 2 do
+ @object.increment
+ end
+ end
+ end
+
+ def test_assert_changes_with_from_option_and_to_option
+ assert_changes '@object.num', from: 0, to: 1 do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_from_and_to_options_and_wrong_to_value
+ assert_raises Minitest::Assertion do
+ assert_changes '@object.num', from: 0, to: 2 do
+ @object.increment
+ end
+ end
+ end
+
+ def test_assert_changes_works_with_any_object
+ retval = silence_warnings do
+ assert_changes :@new_object, from: nil, to: 42 do
+ @new_object = 42
+ end
+ end
+
+ assert_equal 42, retval
+ end
+
+ def test_assert_changes_works_with_nil
+ oldval = @object
+
+ retval = assert_changes :@object, from: oldval, to: nil do
+ @object = nil
+ end
+
+ assert_nil retval
+ end
+
+ def test_assert_changes_with_to_and_case_operator
+ token = nil
+
+ assert_changes 'token', to: /\w{32}/ do
+ token = SecureRandom.hex
+ end
+ end
+
+ def test_assert_changes_with_to_and_from_and_case_operator
+ token = SecureRandom.hex
+
+ assert_changes 'token', from: /\w{32}/, to: /\w{32}/ do
+ token = SecureRandom.hex
+ end
+ end
+
+ def test_assert_no_changes_pass
+ assert_no_changes '@object.num' do
+ # ...
+ end
+ end
+
+ def test_assert_no_changes_with_message
+ error = assert_raises Minitest::Assertion do
+ assert_no_changes '@object.num', '@object.num should not change' do
+ @object.increment
+ end
+ end
+
+ assert_equal "@object.num should not change.\n\"@object.num\" did change to 1.\nExpected: 0\n Actual: 1", error.message
+ end
end
class AlsoDoingNothingTest < ActiveSupport::TestCase