summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorryan <ryan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2011-08-23 21:47:25 +0000
committerryan <ryan@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2011-08-23 21:47:25 +0000
commit2c43b9664b29f76c73ac8bae5400b79d0a5313e0 (patch)
tree6e9b4a581ac460c38d22de2750939fa815a98295 /lib
parent885f5fa2b0c1ba20a26759e65fefeece48cb56db (diff)
Imported minitest 2.5.0 (r6557)
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@33036 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/minitest/README.txt270
-rw-r--r--lib/minitest/mock.rb59
-rw-r--r--lib/minitest/pride.rb76
-rw-r--r--lib/minitest/spec.rb393
-rw-r--r--lib/minitest/unit.rb135
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