module MeowTest Failure = Class.new(StandardError) @tests = [] class << self attr_reader :tests, :last_result def test(name, &block) @tests << [name, block] end def assert(value, message = "Expected value to be truthy") raise Failure, message unless value end def refute(value, message = "Expected value to be falsey") raise Failure, message if value end def assert_equal(expected, actual, message = nil) return if expected == actual detail = "Expected #{expected.inspect}, got #{actual.inspect}" raise Failure, [message, detail].compact.join(": ") end def assert_empty(value, message = nil) assert(value.empty?, message || "Expected #{value.inspect} to be empty") end def run! failures = [] @tests.each do |name, test| test.call puts "PASS #{name}" rescue StandardError => error failures << [name, error] puts "FAIL #{name}: #{error.class}: #{error.message}" end @last_result = { :tests => @tests.length, :passed => @tests.length - failures.length, :failures => failures.length, :failure_details => failures.map { |name, error| "#{name}: #{error.class}: #{error.message}" } } puts "#{@last_result[:passed]}/#{@last_result[:tests]} tests passed" raise Failure, @last_result[:failure_details].join("\n") unless failures.empty? @last_result end end end