- documentation

- moved should to context



git-svn-id: https://svn.thoughtbot.com/plugins/tb_test_helpers/trunk@75 7bbfaf0e-4d1d-0410-9690-a8bb5f8ef2aa
This commit is contained in:
tsaleh 2007-04-05 19:15:56 +00:00
parent 6fd2363b8a
commit 1ecd02914b
6 changed files with 176 additions and 99 deletions

15
README
View File

@ -4,12 +4,13 @@ A collection of Test::Unit helper methods.
Adds helpers for
#. Contexts and should statements
#. Common ActiveRecord model tests
#. A few general purpose assertions
1. context and should statements
1. Common ActiveRecord model tests
1. A few general purpose assertions
== Todo
#. Controller test helpers
#. General code cleanups
#. More options for AR helpers
1. Controller test helpers
1. General code cleanups
1. More options for AR helpers

View File

@ -8,6 +8,16 @@ Rake::TestTask.new do |t|
t.verbose = true
end
# Generate the RDoc documentation
Rake::RDocTask.new { |rdoc|
rdoc.rdoc_dir = 'doc'
rdoc.title = "ThoughtBot Test Helpers -- Making your tests easy on the fingers and eyes"
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.rdoc_files.include('README', 'lib/**/*.rb')
}
desc 'Default: run tests.'
task :default => ['test']

View File

@ -1,19 +1,27 @@
require 'test/unit/ui/console/testrunner'
# Completely stolen from redgreen gem
module Color
COLORS = { :clear => 0, :red => 31, :green => 32, :yellow => 33 }
def self.method_missing(color_name, *args)
#
# Adds colored output to your tests. Specify <tt>color: true</tt> in
# your <tt>test/tb_test_helpers.conf</tt> file to enable.
#
# *Bug*: for some reason, this adds another line of output to the end of
# every rake task, as though there was another (empty) set of tests.
# A fix would be most welcome.
#
module Color
COLORS = { :clear => 0, :red => 31, :green => 32, :yellow => 33 } # :nodoc:
def self.method_missing(color_name, *args) # :nodoc:
color(color_name) + args.first + color(:clear)
end
def self.color(color)
def self.color(color) # :nodoc:
"\e[#{COLORS[color.to_sym]}m"
end
end
module Test
module Unit
class TestResult
module Test # :nodoc:
module Unit # :nodoc:
class TestResult # :nodoc:
alias :old_to_s :to_s
def to_s
if old_to_s =~ /\d+ tests, \d+ assertions, (\d+) failures, (\d+) errors/
@ -22,7 +30,7 @@ module Test
end
end
class AutoRunner
class AutoRunner # :nodoc:
alias :old_initialize :initialize
def initialize(standalone)
old_initialize(standalone)
@ -32,7 +40,7 @@ module Test
end
end
class Failure
class Failure # :nodoc:
alias :old_long_display :long_display
def long_display
# old_long_display.sub('Failure', Color.red('Failure'))
@ -40,7 +48,7 @@ module Test
end
end
class Error
class Error # :nodoc:
alias :old_long_display :long_display
def long_display
# old_long_display.sub('Error', Color.yellow('Error'))
@ -48,9 +56,9 @@ module Test
end
end
module UI
module Console
class RedGreenTestRunner < Test::Unit::UI::Console::TestRunner
module UI # :nodoc:
module Console # :nodoc:
class RedGreenTestRunner < Test::Unit::UI::Console::TestRunner # :nodoc:
def output_single(something, level=NORMAL)
return unless (output?(level))
something = case something

120
lib/context.rb Normal file
View File

@ -0,0 +1,120 @@
module TBTestHelpers # :nodoc:
# = context and should blocks
#
# A context block can exist next to normal <tt>def test_blah</tt> statements,
# meaning you do not have to fully commit to the context/should syntax in a test file. We have been
# using this syntax at ThoughtBot, though, and find it very readable.
#
# A context block can contain setup, should, should_eventually, and teardown blocks.
#
# class UserTest << Test::Unit
# context "a User instance" do
# setup do
# @user = User.find(:first)
# end
#
# should "return its full name"
# assert_equal 'John Doe', @user.full_name
# end
# end
# end
#
# This code will produce the method <tt>"test a User instance should return its full name"</tt> (yes, with spaces in the name).
#
# Contexts may be nested. Nested contexts run their setup blocks from out to in before each test.
# They then run their teardown blocks from in to out after each test.
#
# class UserTest << Test::Unit
# context "a User instance" do
# setup do
# @user = User.find(:first)
# end
#
# should "return its full name"
# assert_equal 'John Doe', @user.full_name
# end
#
# context "with a profile" do
# setup do
# @user.profile = Profile.find(:first)
# end
#
# should "return true when sent :has_profile?"
# assert @user.has_profile?
# end
# end
# end
# end
#
# This code will produce the following methods
# * <tt>"test a User instance should return its full name"</tt>
# * <tt>"test a User instance with a profile should return true when sent :has_profile?"</tt> (which will have both setup blocks run before it.)
#
module Context
def Context.included(other) # :nodoc:
@@context_names = []
@@setup_blocks = []
@@teardown_blocks = []
end
# Creates a context block with the given name.
def context(name, &context_block)
saved_setups = @@setup_blocks.dup
saved_teardowns = @@teardown_blocks.dup
saved_contexts = @@context_names.dup
@@context_names << name
context_block.bind(self).call
@@context_names = saved_contexts
@@setup_blocks = saved_setups
@@teardown_blocks = saved_teardowns
end
# Run before every should block in the current context
def setup(&setup_block)
@@setup_blocks << setup_block
end
# Run after every should block in the current context
def teardown(&teardown_block)
@@teardown_blocks << teardown_block
end
# Defines a test. Can be called either inside our outside of a context.
# Optionally specify <tt>:unimplimented => true</tt> (see should_eventually)
def should(name, opts = {}, &should_block)
test_name = ["test", @@context_names, "should", "#{name}"].flatten.join(' ').to_sym
name_defined = eval("self.instance_methods.include?('#{test_name.to_s.gsub(/['"]/, '\$1')}')", should_block.binding)
raise ArgumentError, "'#{test_name}' is already defined" and return if name_defined
setup_blocks = @@setup_blocks.dup
teardown_blocks = @@teardown_blocks.dup
if opts[:unimplemented]
define_method test_name do |*args|
# XXX find a better way of doing this.
assert true
STDOUT.putc "X" # Tests for this model are missing.
end
else
define_method test_name do |*args|
begin
setup_blocks.each {|b| b.bind(self).call }
should_block.bind(self).call(*args)
ensure
teardown_blocks.reverse.each {|b| b.bind(self).call }
end
end
end
end
# Defines a specification that is not yet implemented.
# Will be displayed as an 'X' when running tests, and failures will not be shown.
def should_eventually(name, &block)
should("eventually #{name}", {:unimplemented => true}, &block)
end
end
end

View File

@ -1,66 +0,0 @@
module TBTestHelpers # :nodoc:
module Should
def Should.included(other) # :nodoc:
@@context_names = []
@@setup_blocks = []
@@teardown_blocks = []
end
# Creates a context block with the given name. The context block can contain setup, should, should_eventually, and teardown blocks.
def context(name, &context_block)
saved_setups = @@setup_blocks.dup
saved_teardowns = @@teardown_blocks.dup
saved_contexts = @@context_names.dup
@@context_names << name
context_block.bind(self).call
@@context_names = saved_contexts
@@setup_blocks = saved_setups
@@teardown_blocks = saved_teardowns
end
# Run before every should block in the current context
def setup(&setup_block)
@@setup_blocks << setup_block
end
# Run after every should block in the current context
def teardown(&teardown_block)
@@teardown_blocks << teardown_block
end
# Defines a specification. Can be called either inside our outside of a context.
def should(name, opts = {}, &should_block)
test_name = ["test", @@context_names, "should", "#{name}"].flatten.join(' ').to_sym
name_defined = eval("self.instance_methods.include?('#{test_name.to_s.gsub(/['"]/, '\$1')}')", should_block.binding)
raise ArgumentError, "'#{test_name}' is already defined" and return if name_defined
setup_blocks = @@setup_blocks.dup
teardown_blocks = @@teardown_blocks.dup
if opts[:unimplemented]
define_method test_name do |*args|
# XXX find a better way of doing this.
assert true
STDOUT.putc "X" # Tests for this model are missing.
end
else
define_method test_name do |*args|
begin
setup_blocks.each {|b| b.bind(self).call }
should_block.bind(self).call(*args)
ensure
teardown_blocks.reverse.each {|b| b.bind(self).call }
end
end
end
end
# Defines a specification that is not yet implemented. Will be displayed as an 'X' when running tests, and failures will not be shown.
def should_eventually(name, &block)
should("eventually #{name}", {:unimplemented => true}, &block)
end
end
end

View File

@ -1,5 +1,5 @@
require 'active_record_helpers'
require 'should'
require 'context'
require 'yaml'
config_file = "tb_test_helpers.conf"
@ -14,7 +14,7 @@ module Test # :nodoc:
class << self
include TBTestHelpers::Should
# Loads all fixture files
# Loads all fixture files (<tt>test/fixtures/*.yml</tt>)
def load_all_fixtures
all_fixtures = Dir.glob(File.join(RAILS_ROOT, "test", "fixtures", "*.yml")).collect do |f|
File.basename(f, '.yml').to_sym
@ -24,12 +24,16 @@ module Test # :nodoc:
end
# Logs a message, tagged with TESTING: and the name of the calling method.
# Prints a message to stdout, tagged with the name of the calling method.
def report!(msg = "")
puts("#{caller.first}: #{msg}")
end
# Ensures that the number of items in the collection changes
# assert_difference(User, :count, 1) { User.create }
# assert_difference(User.packages, :size, 3, true) { User.add_three_packages }
#
# Setting reload to true will call <tt>object.reload</tt> after the block (for ActiveRecord associations)
def assert_difference(object, method, difference, reload = false, msg = nil)
initial_value = object.send(method)
yield
@ -37,12 +41,13 @@ module Test # :nodoc:
assert_equal initial_value + difference, object.send(method), (msg || "#{object}##{method} after block")
end
# Ensures that object.method does not change
# Ensures that object.method does not change. See assert_difference for usage.
def assert_no_difference(object, method, reload = false, msg = nil, &block)
assert_difference(object, method, 0, reload, msg, &block)
end
# asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
# Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
# assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
def assert_same_elements(a1, a2, msg = nil)
[:select, :inject, :size].each do |m|
[a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
@ -54,27 +59,26 @@ module Test # :nodoc:
assert_equal(a1h, a2h, msg)
end
# Asserts that the given collection contains item x. If x is a regular expression, ensure that
# at least one element from the collection matches x.
# assert_contains(['a', '1'], /\d/) => passes
def assert_contains(collection, x, extra_msg = "")
collection = [collection] unless collection.is_a?(Array)
msg = "#{x} not found in #{collection.to_a.inspect} " + extra_msg
case x
when Regexp: assert(collection.detect { |e| e =~ x }, msg)
when String: assert(collection.include?(x), msg)
when Fixnum: assert(collection.include?(x), msg)
else
raise ArgumentError, "Don't know what to do with #{x}"
else assert(collection.include?(x), msg)
end
end
# Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
# none of the elements from the collection match x.
def assert_does_not_contain(collection, x, extra_msg = "")
collection = [collection] unless collection.is_a?(Array)
msg = "#{x} found in #{collection.to_a.inspect} " + extra_msg
case x
when Regexp: assert(!collection.detect { |e| e =~ x }, msg)
when String: assert(!collection.include?(x), msg)
when Fixnum: assert(!collection.include?(x), msg)
else
raise ArgumentError, "Don't know what to do with #{x}"
else assert(!collection.include?(x), msg)
end
end