MicroTest just got smaller
Posted by Mathew Abonyi Sat, 20 Jan 2007 13:16:02 GMT
My little library (the smallest ever?) for bootstrapping and lightning-fast testing has just become smaller. I changed the interface a little: now everything is dependent on method name! So you still have two tests, one which expects true and the other false, and which one is determined by using ‘def should_’ or ‘def should_not_’. Oh happy days!
# A super-small testing suite based on method names.
# It has only two types of expectations: should and should_not.
# These two expectations begin the method name, after which you can
# place anything you like. A 'should_...' method will expect true as
# the result of calling the method. A 'should_not_...' method will,
# conversely, expect false.
#
# This is MicroTest's self-test:
#
# TEST_ROOT = File.dirname(__FILE__) + '/..'
# require TEST_ROOT + '/lib/microtest.rb'
#
# class TestMicroTest < MicroTest
# def should_expect_true
# true
# end
#
# def should_not_expect_false
# false
# end
# end
#
# An example:
#
# class BinaryMicroTest < MicroTest
# def should_exit_cleanly
# `ruby #{MYEXEC}`; $? == 0
# end
# def should_not_exit_cleanly
# `ruby #{MYEXEC} -garbage`; $? == 0
# end
# end
#
class MicroTest
class MicroFailure < StandardError; end
class << self
attr_accessor :passed, :failed, :executed, :planned, :plan, :failures
def run
init
execute
report
end
def init
self.passed, self.failed, self.executed = 0, 0, 0
self.planned, self.plan = harvest
self.failures = []
end
def harvest
plan = []
ObjectSpace.each_object(Class) do |k|
plan << [k, k.instance_methods(false).grep(/^should_/)] if k < MicroTest
end
planned = plan.inject(0) { |c, ary| c += ary[1].size; c }
[planned, plan]
end
def execute
puts "Running #{self.planned} test(s)..."
self.plan.each do |klass, tests|
tests.each do |test|
expectation = (test =~ /^should_not/ ? false : true)
instance = klass.new
r = catch_failures(klass, test) { expect(expectation) { instance.send(test) } }
if r
self.passed += 1
print "."
STDOUT.flush
else
self.failed += 1
print "F"
STDOUT.flush
end
self.executed += 1
end
end
end
def expect(expected, &block)
raise MicroFailure, "should be #{expected}" unless block.call == expected
end
def catch_failures(klass, test)
begin
yield
true
rescue Exception => e
self.failures << [klass, test, e]
false
end
end
def report
puts "\n\n"
puts "%d planned, %d executed, %d passed, %d failed." %
[self.planned, self.executed, self.passed, self.failed]
unless self.failures.empty?
puts "\nFAILURE REPORT\n"
self.failures.each { |f| report_failure(f) }
end
end
def report_failure(f)
puts "Suite: #{f[0]}\nTest: #{f[1]}\nException: #{f[2].class}\nMessage: #{f[2].message}"
puts ("\t" << f[2].backtrace.join("\n\t"))
end
end
end
at_exit do
MicroTest.run
end