Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
# Helps to organize and structure your tests with
# * human readable test names
# * flagging tests as pending
# * structuring the test implementation with blocks
# Please see the documentation of single methods below.
# Written by Vladimir Dobriakov
# See http://blog.geekq.net/2009/11/25/minimalist-testing-ruby/ for explanation
require 'minitest/unit'
class String
# colorization
def colorize(text, color_code)
"\e[#{color_code}m#{text}\e[0m"
end
def red; colorize(self, 31); end
def green; colorize(self, 32); end
def yellow; colorize(self, 33); end
def pink; colorize(self, 35); end
end
class << MiniTest::Unit::TestCase
attr_writer :ignore_failures
attr_writer :human_test_names
def ignore_failures
@ignore_failures ||= []
end
def human_test_names
@human_test_names ||= {}
end
# Creates a test method.
#
# @name [String] Your can use a human readable sentence to name your tests e.g.
# `test 'validation of Japanease VAT numbers` instead of having to write
# `def test_validation_of_japanease_vat_numbers`
#
# @options [Hash] Currently only :tdd option is supported. Imagine, your have
# created a test for the new, not yet existing functionality and would like
# to communicate your intentions to your coworker. So you want to commit the
# change. But you do not want to "break the build". And commenting out tests
# is really dirty - you want to track the progress, not sweep the dirt under
# the carpet. If you define your test like `test 'my test', :tdd => 'in_progress',`
# then the test is always executed and provides feedback but does not make
# the test suit fail.
#
# @block The body of your test. If you do not provide any, then test marked
# as pending.
def test(name, options={}, &block)
test_name = "test_#{name.gsub(' ','_')}"
raise ArgumentError, "#{test_name} is already defined" if
self.instance_methods.include? test_name.to_s
if block
define_method test_name, &block
if options[:tdd] # e.g. :tdd => 'in_progress'
ignore_failures << test_name
end
else
puts "PENDING: #{name}"
end
human_test_names[test_name] = name
end
# Defines test as pending. The test will NOT be executed.
# Use this instead of commenting out tests.
def xtest(name, options={}, &block)
puts "PENDING: #{name}"
end
end
class MiniTest::Unit::TestCase
# A hook. Override this method in your class to do something on every
# failure. E.g. for a web application you could store the content
# of the last response and show it in a browser.
# You can also augment the test log with additional data by returning
# additional message from your function.
# Example:
# def on_failure(e)
# if (last_response rescue false)
# if last_response.body.strip.empty?
# return ' [Empty response body]'
# else
# save_and_open_page if ENV['ASSERT_OPENS_PAGE'] and ENV['ASSERT_OPENS_PAGE']=='true'
# return ''
# end
# end
# end
def on_failure(e)
end
# Please set the VERBOSE_TEST environment variable to show single test steps
# somewhat similar to RSpec and stories.
def verbose_mode?
ENV['VERBOSE_TEST'] && ENV['VERBOSE_TEST'].downcase == 'true'
end
# Supports test driven development by enabling non-critical failures
# for features in_progress. No temporary uncommenting needed then.
# See also TestCase::test
def bak_run(result)
method_name = self.name[/^[^(]+/]
name_to_show = self.class.human_test_names[method_name] || method_name
puts "\n\nTEST #{name_to_show } (#{self.class.name})\n" if verbose_mode?
yield(STARTED, self.name)
@_result = result
begin
setup
__send__(@method_name)
rescue Test::Unit::AssertionFailedError => e
msg = e.message
msg << on_failure(e).to_s
method_name = $1 if name =~ /(.*)\(/
if method_name and self.class.ignore_failures and self.class.ignore_failures.include? method_name
puts "\nNon-critical " +
Test::Unit::Failure.new(name, filter_backtrace(e.backtrace), msg).long_display
puts '--'
else
add_failure(msg, e.backtrace)
end
rescue Exception
raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
add_error($!)
ensure
begin
teardown
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception
raise if PASSTHROUGH_EXCEPTIONS.include? $!.class
add_error($!)
end
end
result.add_run
yield(FINISHED, name)
end
# See also `expect`.
def prepare(msg)
puts "Prepare #{msg}\n".green if verbose_mode?
yield if block_given?
end
# Allows for structuring the implementation of your tests - offers an
# abstraction level between the test method and implemenatation statements.
# You can also use it to fold your code in the editor.
# Extends the message of the failed assertions.
# See also `prepare`.
def expect(msg)
begin
yield if block_given?
puts "#{msg}, as expected\n".green if verbose_mode?
rescue Exception => e
puts "#{msg} - does not work\n".red if verbose_mode?
extended = Exception.new("#{msg}: #{e.message}")
extended.set_backtrace e.backtrace
raise extended
end
end
# Makes the difference between hashes easy to grasp for humans.
# Compare it to the original implementation, which just prints both hashes
# after each other.
def assert_hashes_equal(expected, actual, msg=nil)
expected_keys = expected.keys.sort
actual_keys = actual.keys.sort
if !(too_many = actual_keys - expected_keys).empty?
flunk "Too many keys in the actual Hash: #{too_many.inspect}. #{msg}"
end
if !(missing = actual_keys - expected_keys).empty?
flunk "Missing keys in the actual Hash: #{missing.inspect}. #{msg}"
end
expected.each do |key, value|
assert_equal value, actual[key], "key '#{key}' #{msg}"
end
assert_equal expected, actual, msg
end
# Makes the difference between arrays easy to grasp for humans.
# Compare it to the original implementation, which just prints both arrays
# after each other.
def assert_arrays_equal(expected, actual, msg=nil)
assert_equal expected.length, actual.length, "Arrays of same length expected. #{msg}"
expected.each_with_index do |value, i|
assert_equal value, actual[i], "Array element #{i}. #{msg}"
end
end
# Copied from Ruby 1.9 Dir.mktmpdir
# Security relaxed to make it work with JRuby 1.3
def mktmpdir(prefix_suffix=nil, tmpdir=nil)
case prefix_suffix
when nil
prefix = "d"
suffix = ""
when String
prefix = prefix_suffix
suffix = ""
when Array
prefix = prefix_suffix[0]
suffix = prefix_suffix[1]
else
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
end
tmpdir ||= Dir.tmpdir
t = Time.now.strftime("%Y%m%d")
n = nil
begin
path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
path << "-#{n}" if n
path << suffix
Dir.mkdir(path, 0700)
rescue Errno::EEXIST
n ||= 0
n += 1
retry
end
if block_given?
begin
yield path
ensure
FileUtils.remove_entry path
end
else
path
end
end
# Modify and later restore an environment variable
def modify_env(name, value)
old = ENV[name]
begin
ENV[name] = value
yield if block_given?
ensure
ENV[name] = old
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment