diff options
Diffstat (limited to 'lib/minitest')
-rw-r--r-- | lib/minitest/README.txt | 270 | ||||
-rw-r--r-- | lib/minitest/mock.rb | 59 | ||||
-rw-r--r-- | lib/minitest/pride.rb | 76 | ||||
-rw-r--r-- | lib/minitest/spec.rb | 393 | ||||
-rw-r--r-- | lib/minitest/unit.rb | 135 |
5 files changed, 764 insertions, 169 deletions
diff --git a/lib/minitest/README.txt b/lib/minitest/README.txt new file mode 100644 index 0000000000..0595083d49 --- /dev/null +++ b/lib/minitest/README.txt @@ -0,0 +1,270 @@ += minitest/{unit,spec,mock,benchmark} + +home :: https://github.com/seattlerb/minitest +rdoc :: http://bfts.rubyforge.org/minitest + +== DESCRIPTION: + +minitest provides a complete suite of testing facilities supporting +TDD, BDD, mocking, and benchmarking. + +minitest/unit is a small and incredibly fast unit testing framework. +It provides a rich set of assertions to make your tests clean and +readable. + +minitest/spec is a functionally complete spec engine. It hooks onto +minitest/unit and seamlessly bridges test assertions over to spec +expectations. + +minitest/benchmark is an awesome way to assert the performance of your +algorithms in a repeatable manner. Now you can assert that your newb +co-worker doesn't replace your linear algorithm with an exponential +one! + +minitest/mock by Steven Baker, is a beautifully tiny mock object +framework. + +minitest/pride shows pride in testing and adds coloring to your test +output. I guess it is an example of how to write IO pipes too. :P + +minitest/unit is meant to have a clean implementation for language +implementors that need a minimal set of methods to bootstrap a working +test suite. For example, there is no magic involved for test-case +discovery. + +== FEATURES/PROBLEMS: + +* minitest/autorun - the easy and explicit way to run all your tests. +* minitest/unit - a very fast, simple, and clean test system. +* minitest/spec - a very fast, simple, and clean spec system. +* minitest/mock - a simple and clean mock system. +* minitest/benchmark - an awesome way to assert your algorithm's performance. +* minitest/pride - show your pride in testing! +* Incredibly small and fast runner, but no bells and whistles. + +== RATIONALE: + +See design_rationale.rb to see how specs and tests work in minitest. + +== SYNOPSIS: + +Given that you'd like to test the following class: + + class Meme + def i_can_has_cheezburger? + "OHAI!" + end + + def will_it_blend? + "YES!" + end + end + +=== Unit tests + + require 'minitest/autorun' + + class TestMeme < MiniTest::Unit::TestCase + def setup + @meme = Meme.new + end + + def test_that_kitty_can_eat + assert_equal "OHAI!", @meme.i_can_has_cheezburger? + end + + def test_that_it_will_not_blend + refute_match /^no/i, @meme.will_it_blend? + end + end + +=== Specs + + require 'minitest/autorun' + + describe Meme do + before do + @meme = Meme.new + end + + describe "when asked about cheeseburgers" do + it "must respond positively" do + @meme.i_can_has_cheezburger?.must_equal "OHAI!" + end + end + + describe "when asked about blending possibilities" do + it "won't say no" do + @meme.will_it_blend?.wont_match /^no/i + end + end + end + +=== Benchmarks + +Add benchmarks to your regular unit tests. If the unit tests fail, the +benchmarks won't run. + + # optionally run benchmarks, good for CI-only work! + require 'minitest/benchmark' if ENV["BENCH"] + + class TestMeme < MiniTest::Unit::TestCase + # Override self.bench_range or default range is [1, 10, 100, 1_000, 10_000] + def bench_my_algorithm + assert_performance_linear 0.9999 do |n| # n is a range value + n.times do + @obj.my_algorithm + end + end + end + end + +Or add them to your specs. If you make benchmarks optional, you'll +need to wrap your benchmarks in a conditional since the methods won't +be defined. + + describe Meme do + if ENV["BENCH"] then + bench_performance_linear "my_algorithm", 0.9999 do |n| + 100.times do + @obj.my_algorithm(n) + end + end + end + end + +outputs something like: + + # Running benchmarks: + + TestBlah 100 1000 10000 + bench_my_algorithm 0.006167 0.079279 0.786993 + bench_other_algorithm 0.061679 0.792797 7.869932 + +Output is tab-delimited to make it easy to paste into a spreadsheet. + +=== Mocks + + class MemeAsker + def initialize(meme) + @meme = meme + end + + def ask(question) + method = question.tr(" ","_") + "?" + @meme.send(method) + end + end + + require 'minitest/autorun' + + describe MemeAsker do + before do + @meme = MiniTest::Mock.new + @meme_asker = MemeAsker.new @meme + end + + describe "#ask" do + describe "when passed an unpunctuated question" do + it "should invoke the appropriate predicate method on the meme" do + @meme.expect :will_it_blend?, :return_value + @meme_asker.ask "will it blend" + @meme.verify + end + end + end + end + +=== Customizable Test Runner Types: + +MiniTest::Unit.runner=(runner) provides an easy way of creating custom +test runners for specialized needs. Justin Weiss provides the +following real-world example to create an alternative to regular +fixture loading: + + class MiniTestWithHooks::Unit < MiniTest::Unit + def before_suites + end + + def after_suites + end + + def _run_suites(suites, type) + begin + before_suites + super(suites, type) + ensure + after_suites + end + end + + def _run_suite(suite, type) + begin + suite.before_suite + super(suite, type) + ensure + suite.after_suite + end + end + end + + module MiniTestWithTransactions + class Unit < MiniTestWithHooks::Unit + include TestSetupHelper + + def before_suites + super + setup_nested_transactions + # load any data we want available for all tests + end + + def after_suites + teardown_nested_transactions + super + end + end + end + + MiniTest::Unit.runner = MiniTestWithTransactions::Unit.new + +== REQUIREMENTS: + +* Ruby 1.8, maybe even 1.6 or lower. No magic is involved. + +== INSTALL: + + sudo gem install minitest + +On 1.9, you already have it. To get newer candy you can still install +the gem, but you'll need to activate the gem explicitly to use it: + + require 'rubygems' + gem 'minitest' # ensures you're using the gem, and not the built in MT + require 'minitest/autorun' + + # ... usual testing stuffs ... + +== LICENSE: + +(The MIT License) + +Copyright (c) Ryan Davis, seattle.rb + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/minitest/mock.rb b/lib/minitest/mock.rb index 1cbf18b19f..c342c04995 100644 --- a/lib/minitest/mock.rb +++ b/lib/minitest/mock.rb @@ -15,20 +15,39 @@ module MiniTest # All mock objects are an instance of Mock class Mock + alias :__respond_to? :respond_to? + + skip_methods = %w(object_id respond_to_missing? inspect === to_s) + + instance_methods.each do |m| + undef_method m unless skip_methods.include?(m.to_s) || m =~ /^__/ + end + def initialize # :nodoc: @expected_calls = {} @actual_calls = Hash.new {|h,k| h[k] = [] } end ## - # Expect that method +name+ is called, optionally with +args+, and - # returns +retval+. + # Expect that method +name+ is called, optionally with +args+, and returns + # +retval+. # # @mock.expect(:meaning_of_life, 42) # @mock.meaning_of_life # => 42 # # @mock.expect(:do_something_with, true, [some_obj, true]) # @mock.do_something_with(some_obj, true) # => true + # + # +args+ is compared to the expected args using case equality (ie, the + # '===' operator), allowing for less specific expectations. + # + # @mock.expect(:uses_any_string, true, [String]) + # @mock.uses_any_string("foo") # => true + # @mock.verify # => true + # + # @mock.expect(:uses_one_string, true, ["foo"] + # @mock.uses_one_string("bar") # => true + # @mock.verify # => raises MockExpectationError def expect(name, retval, args=[]) @expected_calls[name] = { :retval => retval, :args => args } @@ -43,25 +62,45 @@ module MiniTest def verify @expected_calls.each_key do |name| expected = @expected_calls[name] - msg = "expected #{name}, #{expected.inspect}" - raise MockExpectationError, msg unless + msg1 = "expected #{name}, #{expected.inspect}" + msg2 = "#{msg1}, got #{@actual_calls[name].inspect}" + + raise MockExpectationError, msg2 if + @actual_calls.has_key? name and + not @actual_calls[name].include?(expected) + + raise MockExpectationError, msg1 unless @actual_calls.has_key? name and @actual_calls[name].include?(expected) end true end def method_missing(sym, *args) # :nodoc: - raise NoMethodError unless @expected_calls.has_key?(sym) - raise ArgumentError unless @expected_calls[sym][:args].size == args.size - retval = @expected_calls[sym][:retval] - @actual_calls[sym] << { :retval => retval, :args => args } + expected = @expected_calls[sym] + + unless expected then + raise NoMethodError, "unmocked method %p, expected one of %p" % + [sym, @expected_calls.keys.sort_by(&:to_s)] + end + + expected_args, retval = expected[:args], expected[:retval] + + unless expected_args.size == args.size + raise ArgumentError, "mocked method %p expects %d arguments, got %d" % + [sym, expected[:args].size, args.size] + end + + @actual_calls[sym] << { + :retval => retval, + :args => expected_args.zip(args).map { |mod, a| mod if mod === a } + } + retval end - alias :original_respond_to? :respond_to? def respond_to?(sym) # :nodoc: return true if @expected_calls.has_key?(sym) - return original_respond_to?(sym) + return __respond_to?(sym) end end end diff --git a/lib/minitest/pride.rb b/lib/minitest/pride.rb index 9a4d68859a..ac7745695c 100644 --- a/lib/minitest/pride.rb +++ b/lib/minitest/pride.rb @@ -10,32 +10,90 @@ require "minitest/unit" # Show your testing pride! class PrideIO - attr_reader :io + ESC = "\e[" + NND = "#{ESC}0m" - # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm - COLORS = (31..36).to_a - CHARS = ["*"] + attr_reader :io def initialize io @io = io - @colors = COLORS.cycle - @chars = CHARS.cycle + # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm + # also reference http://en.wikipedia.org/wiki/ANSI_escape_code + @colors ||= (31..36).to_a + @size = @colors.size + @index = 0 + # io.sync = true end def print o case o when "." then - io.print "\e[#{@colors.next}m#{@chars.next}\e[0m" + io.print pride o when "E", "F" then - io.print "\e[41m\e[37m#{o}\e[0m" + io.print "#{ESC}41m#{ESC}37m#{o}#{NND}" else io.print o end end + def puts(*o) + o.map! { |s| + s.sub(/Finished tests/) { + @index = 0 + 'Fabulous tests'.split(//).map { |c| + pride(c) + }.join + } + } + + super + end + + def pride string + string = "*" if string == "." + c = @colors[@index % @size] + @index += 1 + "#{ESC}#{c}m#{string}#{NND}" + end + def method_missing msg, *args io.send(msg, *args) end end -MiniTest::Unit.output = PrideIO.new(MiniTest::Unit.output) +class PrideLOL < PrideIO # inspired by lolcat, but massively cleaned up + PI_3 = Math::PI / 3 + + def initialize io + # walk red, green, and blue around a circle separated by equal thirds. + # + # To visualize, type this into wolfram-alpha: + # + # plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3) + + # 6 has wide pretty gradients. 3 == lolcat, about half the width + @colors = (0...(6 * 7)).map { |n| + n *= 1.0 / 6 + r = (3 * Math.sin(n ) + 3).to_i + g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i + b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i + + # Then we take rgb and encode them in a single number using base 6. + # For some mysterious reason, we add 16... to clear the bottom 4 bits? + # Yes... they're ugly. + + 36 * r + 6 * g + b + 16 + } + + super + end + + def pride string + c = @colors[@index % @size] + @index += 1 + "#{ESC}38;5;#{c}m#{string}#{NND}" + end +end + +klass = ENV['TERM'] =~ /^xterm(-256color)?$/ ? PrideLOL : PrideIO +MiniTest::Unit.output = klass.new(MiniTest::Unit.output) diff --git a/lib/minitest/spec.rb b/lib/minitest/spec.rb index 4b16cd03ec..7b414e324a 100644 --- a/lib/minitest/spec.rb +++ b/lib/minitest/spec.rb @@ -8,7 +8,7 @@ require 'minitest/unit' -class Module +class Module # :nodoc: def infect_an_assertion meth, new_name, dont_flip = false # :nodoc: # warn "%-22p -> %p %p" % [meth, new_name, dont_flip] self.class_eval <<-EOM @@ -23,39 +23,18 @@ class Module end ## - # Create your own expectations from MiniTest::Assertions using a - # flexible set of rules. If you don't like must/wont, then this - # method is your friend. For an example of its usage see the bottom - # of minitest/spec.rb. + # infect_with_assertions has been removed due to excessive clever. + # Use infect_an_assertion directly instead. def infect_with_assertions(pos_prefix, neg_prefix, skip_re, dont_flip_re = /\c0/, map = {}) - MiniTest::Assertions.public_instance_methods(false).sort.each do |meth| - meth = meth.to_s - - new_name = case meth - when /^assert/ then - meth.sub(/^assert/, pos_prefix.to_s) - when /^refute/ then - meth.sub(/^refute/, neg_prefix.to_s) - end - next unless new_name - next if new_name =~ skip_re - - regexp, replacement = map.find { |re, _| new_name =~ re } - new_name.sub! regexp, replacement if replacement - - puts "\n##\n# :method: #{new_name}\n# See MiniTest::Assertions##{meth}" if - $0 == __FILE__ - - infect_an_assertion meth, new_name, new_name =~ dont_flip_re - end + abort "infect_with_assertions is dead. Use infect_an_assertion directly" end end -module Kernel +module Kernel # :nodoc: ## # Describe a series of expectations for a given target +desc+. # @@ -80,25 +59,16 @@ module Kernel # end # end - def describe desc, &block # :doc: + def describe desc, additional_desc = nil, &block # :doc: stack = MiniTest::Spec.describe_stack - name = [stack.last, desc].compact.join("::") + name = [stack.last, desc, additional_desc].compact.join("::") sclas = stack.last || if Class === self && self < MiniTest::Spec then self else MiniTest::Spec.spec_type desc end - cls = Class.new sclas - - sclas.children << cls unless cls == MiniTest::Spec - # :stopdoc: - # omg this sucks - (class << cls; self; end).send(:define_method, :to_s) { name } - (class << cls; self; end).send(:define_method, :desc) { desc } - # :startdoc: - - cls.nuke_test_methods! + cls = sclas.create name, desc stack.push cls cls.class_eval(&block) @@ -111,7 +81,7 @@ end ## # MiniTest::Spec -- The faster, better, less-magical spec framework! # -# For a list of expectations, see Object. +# For a list of expectations, see MiniTest::Expectations. class MiniTest::Spec < MiniTest::Unit::TestCase ## @@ -151,6 +121,9 @@ class MiniTest::Spec < MiniTest::Unit::TestCase @@current_spec end + ## + # Returns the children of this spec. + def self.children @children ||= [] end @@ -167,25 +140,6 @@ class MiniTest::Spec < MiniTest::Unit::TestCase end ## - # Spec users want setup/teardown to be inherited and NOTHING ELSE. - # It is almost like method reuse is lost on them. - - def self.define_inheritable_method name, &block # :nodoc: - # regular super() warns - super_method = self.superclass.instance_method name - - teardown = name.to_s == "teardown" - super_before = super_method && ! teardown - super_after = super_method && teardown - - define_method name do - super_method.bind(self).call if super_before - instance_eval(&block) - super_method.bind(self).call if super_after - end - end - - ## # Define a 'before' action. Inherits the way normal methods should. # # NOTE: +type+ is ignored and is only there to make porting easier. @@ -194,7 +148,8 @@ class MiniTest::Spec < MiniTest::Unit::TestCase def self.before type = :each, &block raise "unsupported before type: #{type}" unless type == :each - define_inheritable_method :setup, &block + + add_setup_hook {|tc| tc.instance_eval(&block) } end ## @@ -206,7 +161,8 @@ class MiniTest::Spec < MiniTest::Unit::TestCase def self.after type = :each, &block raise "unsupported after type: #{type}" unless type == :each - define_inheritable_method :teardown, &block + + add_teardown_hook {|tc| tc.instance_eval(&block) } end ## @@ -232,154 +188,311 @@ class MiniTest::Spec < MiniTest::Unit::TestCase mod.send :undef_method, name if mod.public_method_defined? name end end -end -Object.infect_with_assertions(:must, :wont, - /^(must|wont)$|wont_(throw)| - must_(block|not?_|nothing|raise$)/x, - /(must|wont)_(include|respond_to)/, - /(must_throw)s/ => '\1', - /(?!not)_same/ => '_be_same_as', - /_in_/ => '_be_within_', - /_operator/ => '_be', - /_includes/ => '_include', - /(must|wont)_(.*_of|nil|silent|empty)/ => '\1_be_\2', - /must_raises/ => 'must_raise') + def self.let name, &block + define_method name do + @_memoized ||= {} + @_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) } + end + end -class Object - alias :must_be_close_to :must_be_within_delta - alias :wont_be_close_to :wont_be_within_delta + def self.subject &block + let :subject, &block + end + + def self.create name, desc # :nodoc: + cls = Class.new(self) do + @name = name + @desc = desc - if $0 == __FILE__ then - { "must" => "assert", "wont" => "refute" }.each do |a, b| - puts "\n" - puts "##" - puts "# :method: #{a}_be_close_to" - puts "# See MiniTest::Assertions##{b}_in_delta" + nuke_test_methods! end + + children << cls + + cls end - ## - # :method: must_be - # See MiniTest::Assertions#assert_operator + def self.to_s # :nodoc: + defined?(@name) ? @name : super + end - ## - # :method: must_be_close_to - # See MiniTest::Assertions#assert_in_delta + # :stopdoc: + class << self + attr_reader :name, :desc + end + # :startdoc: +end +module MiniTest::Expectations ## + # See MiniTest::Assertions#assert_empty. + # + # collection.must_be_empty + # # :method: must_be_empty - # See MiniTest::Assertions#assert_empty - ## - # :method: must_be_instance_of - # See MiniTest::Assertions#assert_instance_of + infect_an_assertion :assert_empty, :must_be_empty ## - # :method: must_be_kind_of - # See MiniTest::Assertions#assert_kind_of + # See MiniTest::Assertions#assert_equal + # + # a.must_equal b + # + # :method: must_equal + + infect_an_assertion :assert_equal, :must_equal ## - # :method: must_be_nil - # See MiniTest::Assertions#assert_nil + # See MiniTest::Assertions#assert_in_delta + # + # n.must_be_close_to m [, delta] + # + # :method: must_be_within_delta + + infect_an_assertion :assert_in_delta, :must_be_close_to + + alias :must_be_within_delta :must_be_close_to ## - # :method: must_be_same_as - # See MiniTest::Assertions#assert_same + # See MiniTest::Assertions#assert_in_epsilon + # + # n.must_be_within_epsilon m [, epsilon] + # + # :method: must_be_within_epsilon + + infect_an_assertion :assert_in_epsilon, :must_be_within_epsilon ## - # :method: must_be_silent - # See MiniTest::Assertions#assert_silent + # See MiniTest::Assertions#assert_includes + # + # collection.must_include obj + # + # :method: must_include + + infect_an_assertion :assert_includes, :must_include, :reverse ## - # :method: must_be_within_delta - # See MiniTest::Assertions#assert_in_delta + # See MiniTest::Assertions#assert_instance_of + # + # obj.must_be_instance_of klass + # + # :method: must_be_instance_of + + infect_an_assertion :assert_instance_of, :must_be_instance_of ## - # :method: must_be_within_epsilon - # See MiniTest::Assertions#assert_in_epsilon + # See MiniTest::Assertions#assert_kind_of + # + # obj.must_be_kind_of mod + # + # :method: must_be_kind_of + + infect_an_assertion :assert_kind_of, :must_be_kind_of ## - # :method: must_equal - # See MiniTest::Assertions#assert_equal + # See MiniTest::Assertions#assert_match + # + # a.must_match b + # + # :method: must_match + + infect_an_assertion :assert_match, :must_match ## - # :method: must_include - # See MiniTest::Assertions#assert_includes + # See MiniTest::Assertions#assert_nil + # + # obj.must_be_nil + # + # :method: must_be_nil + + infect_an_assertion :assert_nil, :must_be_nil ## - # :method: must_match - # See MiniTest::Assertions#assert_match + # See MiniTest::Assertions#assert_operator + # + # n.must_be :<=, 42 + # + # :method: must_be + + infect_an_assertion :assert_operator, :must_be ## - # :method: must_output # See MiniTest::Assertions#assert_output + # + # proc { ... }.must_output out_or_nil [, err] + # + # :method: must_output + + infect_an_assertion :assert_output, :must_output ## - # :method: must_raise # See MiniTest::Assertions#assert_raises + # + # proc { ... }.must_raise exception + # + # :method: must_raise + + infect_an_assertion :assert_raises, :must_raise ## - # :method: must_respond_to # See MiniTest::Assertions#assert_respond_to + # + # obj.must_respond_to msg + # + # :method: must_respond_to + + infect_an_assertion :assert_respond_to, :must_respond_to, :reverse ## - # :method: must_send - # See MiniTest::Assertions#assert_send + # See MiniTest::Assertions#assert_same + # + # a.must_be_same_as b + # + # :method: must_be_same_as + + infect_an_assertion :assert_same, :must_be_same_as ## - # :method: must_throw - # See MiniTest::Assertions#assert_throws + # See MiniTest::Assertions#assert_send + # TODO: remove me + # + # a.must_send + # + # :method: must_send + + infect_an_assertion :assert_send, :must_send ## - # :method: wont_be - # See MiniTest::Assertions#refute_operator + # See MiniTest::Assertions#assert_silent + # + # proc { ... }.must_be_silent + # + # :method: must_be_silent + + infect_an_assertion :assert_silent, :must_be_silent ## - # :method: wont_be_close_to - # See MiniTest::Assertions#refute_in_delta + # See MiniTest::Assertions#assert_throws + # + # proc { ... }.must_throw sym + # + # :method: must_throw + + infect_an_assertion :assert_throws, :must_throw ## - # :method: wont_be_empty # See MiniTest::Assertions#refute_empty + # + # collection.wont_be_empty + # + # :method: wont_be_empty + + infect_an_assertion :refute_empty, :wont_be_empty ## - # :method: wont_be_instance_of - # See MiniTest::Assertions#refute_instance_of + # See MiniTest::Assertions#refute_equal + # + # a.wont_equal b + # + # :method: wont_equal + + infect_an_assertion :refute_equal, :wont_equal ## - # :method: wont_be_kind_of - # See MiniTest::Assertions#refute_kind_of + # See MiniTest::Assertions#refute_in_delta + # + # n.wont_be_close_to m [, delta] + # + # :method: wont_be_within_delta + + infect_an_assertion :refute_in_delta, :wont_be_within_delta + + alias :wont_be_close_to :wont_be_within_delta + # FIX: reverse aliases ## - # :method: wont_be_nil - # See MiniTest::Assertions#refute_nil + # See MiniTest::Assertions#refute_in_epsilon + # + # n.wont_be_within_epsilon m [, epsilon] + # + # :method: wont_be_within_epsilon + + infect_an_assertion :refute_in_epsilon, :wont_be_within_epsilon ## - # :method: wont_be_same_as - # See MiniTest::Assertions#refute_same + # See MiniTest::Assertions#refute_includes + # + # collection.wont_include obj + # + # :method: wont_include + + infect_an_assertion :refute_includes, :wont_include, :reverse ## - # :method: wont_be_within_delta - # See MiniTest::Assertions#refute_in_delta + # See MiniTest::Assertions#refute_instance_of + # + # obj.wont_be_instance_of klass + # + # :method: wont_be_instance_of + + infect_an_assertion :refute_instance_of, :wont_be_instance_of ## - # :method: wont_be_within_epsilon - # See MiniTest::Assertions#refute_in_epsilon + # See MiniTest::Assertions#refute_kind_of + # + # obj.wont_be_kind_of mod + # + # :method: wont_be_kind_of + + infect_an_assertion :refute_kind_of, :wont_be_kind_of ## - # :method: wont_equal - # See MiniTest::Assertions#refute_equal + # See MiniTest::Assertions#refute_match + # + # a.wont_match b + # + # :method: wont_match + + infect_an_assertion :refute_match, :wont_match ## - # :method: wont_include - # See MiniTest::Assertions#refute_includes + # See MiniTest::Assertions#refute_nil + # + # obj.wont_be_nil + # + # :method: wont_be_nil + + infect_an_assertion :refute_nil, :wont_be_nil ## - # :method: wont_match - # See MiniTest::Assertions#refute_match + # See MiniTest::Assertions#refute_operator + # + # n.wont_be :<=, 42 + # + # :method: wont_be + + infect_an_assertion :refute_operator, :wont_be ## - # :method: wont_respond_to # See MiniTest::Assertions#refute_respond_to + # + # obj.wont_respond_to msg + # + # :method: wont_respond_to + + infect_an_assertion :refute_respond_to, :wont_respond_to, :reverse + + ## + # See MiniTest::Assertions#refute_same + # + # a.wont_be_same_as b + # + # :method: wont_be_same_as + + infect_an_assertion :refute_same, :wont_be_same_as +end + +class Object + include MiniTest::Expectations end diff --git a/lib/minitest/unit.rb b/lib/minitest/unit.rb index 199360e644..7fda1a0e22 100644 --- a/lib/minitest/unit.rb +++ b/lib/minitest/unit.rb @@ -152,7 +152,7 @@ module MiniTest def mu_pp obj s = obj.inspect - s = s.force_encoding Encoding.default_external if defined? Encoding + s = s.encode Encoding.default_external if defined? Encoding s end @@ -491,7 +491,7 @@ module MiniTest # Fails if +obj+ is empty. def refute_empty obj, msg = nil - msg = message(msg) { "Expected #{obj.inspect} to not be empty" } + msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" } assert_respond_to obj, :empty? refute obj.empty?, msg end @@ -577,7 +577,7 @@ module MiniTest end ## - # Fails if +o1+ is not +op+ +o2+ nil. eg: + # Fails if +o1+ is not +op+ +o2+. Eg: # # refute_operator 1, :>, 2 #=> pass # refute_operator 1, :<, 2 #=> fail @@ -620,7 +620,7 @@ module MiniTest end class Unit - VERSION = "2.2.2" # :nodoc: + VERSION = "2.5.0" # :nodoc: attr_accessor :report, :failures, :errors, :skips # :nodoc: attr_accessor :test_count, :assertion_count # :nodoc: @@ -945,6 +945,7 @@ module MiniTest begin @passed = nil self.setup + self.run_setup_hooks self.__send__ self.__name__ result = "." unless io? @passed = true @@ -955,6 +956,7 @@ module MiniTest result = runner.puke self.class, self.__name__, e ensure begin + self.run_teardown_hooks self.teardown rescue *PASSTHROUGH_EXCEPTIONS raise @@ -987,16 +989,24 @@ module MiniTest reset + ## + # Call this at the top of your tests when you absolutely + # positively need to have ordered tests. In doing so, you're + # admitting that you suck and your tests are weak. + + def self.i_suck_and_my_tests_are_order_dependent! + class << self + define_method :test_order do :alpha end + end + end + def self.inherited klass # :nodoc: @@test_suites[klass] = true + klass.reset_setup_teardown_hooks + super end - ## - # Defines test order and is subclassable. Defaults to :random - # but can be overridden to return :alpha if your tests are order - # dependent (read: weak). - - def self.test_order + def self.test_order # :nodoc: :random end @@ -1035,6 +1045,111 @@ module MiniTest def teardown; end + def self.reset_setup_teardown_hooks # :nodoc: + @setup_hooks = [] + @teardown_hooks = [] + end + + reset_setup_teardown_hooks + + ## + # Adds a block of code that will be executed before every TestCase is + # run. Equivalent to +setup+, but usable multiple times and without + # re-opening any classes. + # + # All of the setup hooks will run in order after the +setup+ method, if + # one is defined. + # + # The argument can be any object that responds to #call or a block. + # That means that this call, + # + # MiniTest::TestCase.add_setup_hook { puts "foo" } + # + # ... is equivalent to: + # + # module MyTestSetup + # def call + # puts "foo" + # end + # end + # + # MiniTest::TestCase.add_setup_hook MyTestSetup + # + # The blocks passed to +add_setup_hook+ take an optional parameter that + # will be the TestCase instance that is executing the block. + + def self.add_setup_hook arg=nil, &block + hook = arg || block + @setup_hooks << hook + end + + def self.setup_hooks # :nodoc: + if superclass.respond_to? :setup_hooks then + superclass.setup_hooks + else + [] + end + @setup_hooks + end + + def run_setup_hooks # :nodoc: + self.class.setup_hooks.each do |hook| + if hook.respond_to?(:arity) && hook.arity == 1 + hook.call(self) + else + hook.call + end + end + end + + ## + # Adds a block of code that will be executed after every TestCase is + # run. Equivalent to +teardown+, but usable multiple times and without + # re-opening any classes. + # + # All of the teardown hooks will run in reverse order after the + # +teardown+ method, if one is defined. + # + # The argument can be any object that responds to #call or a block. + # That means that this call, + # + # MiniTest::TestCase.add_teardown_hook { puts "foo" } + # + # ... is equivalent to: + # + # module MyTestTeardown + # def call + # puts "foo" + # end + # end + # + # MiniTest::TestCase.add_teardown_hook MyTestTeardown + # + # The blocks passed to +add_teardown_hook+ take an optional parameter + # that will be the TestCase instance that is executing the block. + + def self.add_teardown_hook arg=nil, &block + hook = arg || block + @teardown_hooks << hook + end + + def self.teardown_hooks # :nodoc: + if superclass.respond_to? :teardown_hooks then + superclass.teardown_hooks + else + [] + end + @teardown_hooks + end + + def run_teardown_hooks # :nodoc: + self.class.teardown_hooks.reverse.each do |hook| + if hook.respond_to?(:arity) && hook.arity == 1 + hook.call(self) + else + hook.call + end + end + end + include MiniTest::Assertions end # class TestCase end # class Unit |