diff options
Diffstat (limited to 'activesupport/lib/active_support/testing')
10 files changed, 192 insertions, 73 deletions
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index 29305e0082..28cf2953bf 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -1,8 +1,8 @@ -require 'active_support/core_ext/object/blank' - 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>. @@ -19,6 +19,17 @@ module ActiveSupport assert !object, message end + # Assertion that the block should not raise an exception. + # + # Passes if evaluated code in the yielded block raises no exception. + # + # assert_nothing_raised do + # perform_service(param: 'no_exception') + # end + def assert_nothing_raised + yield + end + # Test numeric difference between the return value of an expression as a # result of what is evaluated in the yielded block. # @@ -94,6 +105,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.inspect}" + 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 + error = "#{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/lib/active_support/testing/autorun.rb b/activesupport/lib/active_support/testing/autorun.rb index 84c6b89340..3108e3e549 100644 --- a/activesupport/lib/active_support/testing/autorun.rb +++ b/activesupport/lib/active_support/testing/autorun.rb @@ -1,12 +1,9 @@ -gem 'minitest' +gem "minitest" -require 'minitest' +require "minitest" -if Minitest.respond_to?(:run_with_rails_extension) - unless Minitest.run_with_rails_extension - Minitest.run_with_autorun = true - Minitest.autorun - end -else - Minitest.autorun +if Minitest.respond_to?(:run_via) && !Minitest.run_via[:rails] + Minitest.run_via[:ruby] = true end + +Minitest.autorun diff --git a/activesupport/lib/active_support/testing/constant_lookup.rb b/activesupport/lib/active_support/testing/constant_lookup.rb index 07d477c0db..647395d2b3 100644 --- a/activesupport/lib/active_support/testing/constant_lookup.rb +++ b/activesupport/lib/active_support/testing/constant_lookup.rb @@ -44,7 +44,6 @@ module ActiveSupport end end end - end end end diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb index 5dfa14eeba..58911570e8 100644 --- a/activesupport/lib/active_support/testing/deprecation.rb +++ b/activesupport/lib/active_support/testing/deprecation.rb @@ -1,4 +1,5 @@ -require 'active_support/deprecation' +require "active_support/deprecation" +require "active_support/core_ext/regexp" module ActiveSupport module Testing @@ -8,7 +9,7 @@ module ActiveSupport assert !warnings.empty?, "Expected a deprecation warning within the block but received none" if match match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp) - assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}" + assert warnings.any? { |w| match.match?(w) }, "No deprecation warning matched #{match}: #{warnings.join(', ')}" end result end diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index edf8b30a0a..d30b34ecd6 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -1,7 +1,7 @@ module ActiveSupport module Testing module Isolation - require 'thread' + require "thread" def self.included(klass) #:nodoc: klass.class_eval do @@ -13,17 +13,6 @@ module ActiveSupport !ENV["NO_FORK"] && Process.respond_to?(:fork) end - @@class_setup_mutex = Mutex.new - - def _run_class_setup # class setup method should only happen in parent - @@class_setup_mutex.synchronize do - unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST'] - self.class.setup if self.class.respond_to?(:setup) - @@ran_class_setup = true - end - end - end - def run serialized = run_in_isolation do super @@ -79,17 +68,17 @@ module ActiveSupport if ENV["ISOLATION_TEST"] yield File.open(ENV["ISOLATION_OUTPUT"], "w") do |file| - file.puts [Marshal.dump(self.dup)].pack("m") + file.puts [Marshal.dump(dup)].pack("m") end exit! else Tempfile.open("isolation") do |tmpfile| env = { - 'ISOLATION_TEST' => self.class.name, - 'ISOLATION_OUTPUT' => tmpfile.path + "ISOLATION_TEST" => self.class.name, + "ISOLATION_OUTPUT" => tmpfile.path } - load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ") + load_paths = $-I.map { |p| "-I\"#{File.expand_path(p)}\"" }.join(" ") orig_args = ORIG_ARGV.join(" ") test_opts = "-n#{self.class.name}##{self.name}" command = "#{Gem.ruby} #{load_paths} #{$0} '#{orig_args}' #{test_opts}" diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb index fccaa54f40..6b07416fdc 100644 --- a/activesupport/lib/active_support/testing/method_call_assertions.rb +++ b/activesupport/lib/active_support/testing/method_call_assertions.rb @@ -1,4 +1,4 @@ -require 'minitest/mock' +require "minitest/mock" module ActiveSupport module Testing diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 33f2b8dc9b..358c79c321 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -1,5 +1,5 @@ -require 'active_support/concern' -require 'active_support/callbacks' +require "active_support/concern" +require "active_support/callbacks" module ActiveSupport module Testing diff --git a/activesupport/lib/active_support/testing/stream.rb b/activesupport/lib/active_support/testing/stream.rb index 895192ad05..1d06b94559 100644 --- a/activesupport/lib/active_support/testing/stream.rb +++ b/activesupport/lib/active_support/testing/stream.rb @@ -3,40 +3,40 @@ module ActiveSupport module Stream #:nodoc: private - def silence_stream(stream) - old_stream = stream.dup - stream.reopen(IO::NULL) - stream.sync = true - yield - ensure - stream.reopen(old_stream) - old_stream.close - end + def silence_stream(stream) + old_stream = stream.dup + stream.reopen(IO::NULL) + stream.sync = true + yield + ensure + stream.reopen(old_stream) + old_stream.close + end - def quietly - silence_stream(STDOUT) do - silence_stream(STDERR) do - yield + def quietly + silence_stream(STDOUT) do + silence_stream(STDERR) do + yield + end end end - end - def capture(stream) - stream = stream.to_s - captured_stream = Tempfile.new(stream) - stream_io = eval("$#{stream}") - origin_stream = stream_io.dup - stream_io.reopen(captured_stream) + def capture(stream) + stream = stream.to_s + captured_stream = Tempfile.new(stream) + stream_io = eval("$#{stream}") + origin_stream = stream_io.dup + stream_io.reopen(captured_stream) - yield + yield - stream_io.rewind - return captured_stream.read - ensure - captured_stream.close - captured_stream.unlink - stream_io.reopen(origin_stream) - end + stream_io.rewind + return captured_stream.read + ensure + captured_stream.close + captured_stream.unlink + stream_io.reopen(origin_stream) + end end end end diff --git a/activesupport/lib/active_support/testing/tagged_logging.rb b/activesupport/lib/active_support/testing/tagged_logging.rb index 843ce4a867..afdff87b45 100644 --- a/activesupport/lib/active_support/testing/tagged_logging.rb +++ b/activesupport/lib/active_support/testing/tagged_logging.rb @@ -8,7 +8,7 @@ module ActiveSupport def before_setup if tagged_logger && tagged_logger.info? heading = "#{self.class}: #{name}" - divider = '-' * heading.size + divider = "-" * heading.size tagged_logger.info divider tagged_logger.info heading tagged_logger.info divider diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb index fca0947c5b..e2f008b4b7 100644 --- a/activesupport/lib/active_support/testing/time_helpers.rb +++ b/activesupport/lib/active_support/testing/time_helpers.rb @@ -1,32 +1,39 @@ +require "active_support/core_ext/string/strip" # for strip_heredoc +require "concurrent/map" + module ActiveSupport module Testing class SimpleStubs # :nodoc: Stub = Struct.new(:object, :method_name, :original_method) def initialize - @stubs = {} + @stubs = Concurrent::Map.new { |h, k| h[k] = {} } end def stub_object(object, method_name, return_value) - key = [object.object_id, method_name] - - if stub = @stubs[key] + if stub = stubbing(object, method_name) unstub_object(stub) end new_name = "__simple_stub__#{method_name}" - @stubs[key] = Stub.new(object, method_name, new_name) + @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name) object.singleton_class.send :alias_method, new_name, method_name object.define_singleton_method(method_name) { return_value } end def unstub_all! - @stubs.each_value do |stub| - unstub_object(stub) + @stubs.each_value do |object_stubs| + object_stubs.each_value do |stub| + unstub_object(stub) + end end - @stubs = {} + @stubs.clear + end + + def stubbing(object, method_name) + @stubs[object.object_id][method_name] end private @@ -66,7 +73,7 @@ module ActiveSupport # +Date.today+, and +DateTime.now+ to return the time or date passed into this method. # # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 - # travel_to Time.new(2004, 11, 24, 01, 04, 44) + # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44) # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 # Date.current # => Wed, 24 Nov 2004 # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500 @@ -88,11 +95,39 @@ module ActiveSupport # state at the end of the block: # # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 - # travel_to Time.new(2004, 11, 24, 01, 04, 44) do + # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44) do # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 # end # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 def travel_to(date_or_time) + if block_given? && simple_stubs.stubbing(Time, :now) + travel_to_nested_block_call = <<-MSG.strip_heredoc + + Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, 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 + + preferred way to achieve above is: + + travel 2.days do + # 2 days from today + end + + travel 5.days do + # 5 days from today + end + + MSG + raise travel_to_nested_block_call + end + if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime) now = date_or_time.midnight.to_time else @@ -116,7 +151,7 @@ module ActiveSupport # `travel` and `travel_to`. # # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 - # travel_to Time.new(2004, 11, 24, 01, 04, 44) + # travel_to Time.zone.local(2004, 11, 24, 01, 04, 44) # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00 # travel_back # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00 |