- 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:
parent
6fd2363b8a
commit
1ecd02914b
13
README
13
README
|
@ -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
|
||||
|
10
Rakefile
10
Rakefile
|
@ -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']
|
||||
|
||||
|
|
32
lib/color.rb
32
lib/color.rb
|
@ -1,19 +1,27 @@
|
|||
require 'test/unit/ui/console/testrunner'
|
||||
|
||||
# Completely stolen from redgreen gem
|
||||
#
|
||||
# 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 }
|
||||
def self.method_missing(color_name, *args)
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue