Rearranged a bunch of code

This commit is contained in:
Tammer Saleh 2008-06-22 12:03:46 -04:00
parent fc938bb185
commit 804f859435
11 changed files with 498 additions and 262 deletions

View File

@ -6,7 +6,7 @@ require 'rake/rdoctask'
Rake::TestTask.new do |t|
t.libs << 'lib'
t.pattern = 'test/{unit,functional,other}/**/*_test.rb'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end

View File

@ -1,4 +1,4 @@
require 'shoulda/gem/shoulda'
require 'shoulda/context'
require 'shoulda/private_helpers'
require 'shoulda/general'
require 'shoulda/active_record_helpers'
@ -22,6 +22,125 @@ end
require 'shoulda/color' if shoulda_options[:color]
module Shoulda
class << self
attr_accessor :current_context
end
VERSION = '1.1.1'
# = Should statements
#
# Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
# contains all the normal code and assertions you're used to seeing, with the added benefit that
# they can be wrapped inside context blocks (see below).
#
# == Example:
#
# class UserTest << Test::Unit::TestCase
#
# def setup
# @user = User.new("John", "Doe")
# end
#
# should "return its full name"
# assert_equal 'John Doe', @user.full_name
# end
#
# end
#
# ...will produce the following test:
# * <tt>"test: User should return its full name. "</tt>
#
# Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class.
def should(name, &blk)
should_eventually(name) && return unless block_given?
if Shoulda.current_context
Shoulda.current_context.should(name, &blk)
else
context_name = self.name.gsub(/Test/, "")
context = Shoulda::Context.new(context_name, self) do
should(name, &blk)
end
context.build
end
end
# Just like should, but never runs, and instead prints an 'X' in the Test::Unit output.
def should_eventually(name, &blk)
context_name = self.name.gsub(/Test/, "")
context = Shoulda::Context.new(context_name, self) do
should_eventually(name, &blk)
end
context.build
end
# = Contexts
#
# A context block groups should statements under a common set of setup/teardown methods.
# Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
# and readability of your test code.
#
# A context block can contain setup, should, should_eventually, and teardown blocks.
#
# class UserTest << Test::Unit::TestCase
# 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>.
#
# Contexts may be nested. Nested contexts run their setup blocks from out to in before each
# should statement. They then run their teardown blocks from in to out after each should statement.
#
# class UserTest << Test::Unit::TestCase
# 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>
#
# <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt>
# tests</b>. This means you do not have to fully commit to the context/should syntax in a test file.
def context(name, &blk)
if Shoulda.current_context
Shoulda.current_context.context(name, &blk)
else
context = Shoulda::Context.new(name, self, &blk)
context.build
end
end
end
module Test # :nodoc: all
module Unit
class TestCase

View File

@ -1,6 +1,8 @@
require 'test/unit/ui/console/testrunner'
# Completely stolen from redgreen gem
# Completely stolen from redgreen gem (thanks Pat Eyler and Chris Wanstrath).
#
# NOTE: Is this now in autotest? Can I remove this, then?
#
# Adds colored output to your tests. Specify <tt>color: true</tt> in
# your <tt>~/.shoulda.conf</tt> file to enable.

121
lib/shoulda/context.rb Normal file
View File

@ -0,0 +1,121 @@
require File.join(File.dirname(__FILE__), 'extensions', 'proc')
module Shoulda # :nodoc:
class Context
attr_accessor :name # my name
attr_accessor :parent # may be another context, or the original test::unit class.
attr_accessor :subcontexts # array of contexts nested under myself
attr_accessor :setup_block # block given via a setup method
attr_accessor :teardown_block # block given via a teardown method
attr_accessor :shoulds # array of hashes representing the should statements
attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
def initialize(name, parent, &blk)
Shoulda.current_context = self
self.name = name
self.parent = parent
self.setup_block = nil
self.teardown_block = nil
self.shoulds = []
self.should_eventuallys = []
self.subcontexts = []
blk.bind(self).call
Shoulda.current_context = nil
end
def context(name, &blk)
subcontexts << Context.new(name, self, &blk)
Shoulda.current_context = self
end
def setup(&blk)
self.setup_block = blk
end
def teardown(&blk)
self.teardown_block = blk
end
def should(name, &blk)
self.shoulds << { :name => name, :block => blk }
end
def should_eventually(name, &blk)
self.should_eventuallys << { :name => name, :block => blk }
end
def full_name
parent_name = parent.full_name if am_subcontext?
return [parent_name, name].join(" ").strip
end
def am_subcontext?
parent.is_a?(self.class) # my parent is the same class as myself.
end
def test_unit_class
am_subcontext? ? parent.test_unit_class : parent
end
def create_test_from_should_hash(should)
test_name = ["test:", full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym
if test_unit_class.instance_methods.include?(test_name.to_s)
warn " * WARNING: '#{test_name}' is already defined"
end
context = self
test_unit_class.send(:define_method, test_name) do |*args|
begin
context.run_all_setup_blocks(self)
should[:block].bind(self).call
ensure
context.run_all_teardown_blocks(self)
end
end
end
def run_all_setup_blocks(binding)
self.parent.run_all_setup_blocks(binding) if am_subcontext?
setup_block.bind(binding).call if setup_block
end
def run_all_teardown_blocks(binding)
teardown_block.bind(binding).call if teardown_block
self.parent.run_all_teardown_blocks(binding) if am_subcontext?
end
def print_should_eventuallys
should_eventuallys.each do |should|
test_name = [full_name, "should", "#{should[:name]}. "].flatten.join(' ')
puts " * DEFERRED: " + test_name
end
subcontexts.each { |context| context.print_should_eventuallys }
end
def build
shoulds.each do |should|
create_test_from_should_hash(should)
end
subcontexts.each { |context| context.build }
print_should_eventuallys
end
def method_missing(method, *args, &blk)
test_unit_class.send(method, *args, &blk)
end
end
end
module Test # :nodoc: all
module Unit
class TestCase
extend Shoulda
end
end
end

View File

@ -0,0 +1,17 @@
begin
require 'active_support'
rescue LoadError
# Stolen straight from ActiveSupport
class Proc #:nodoc:
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end
end

View File

@ -1,14 +0,0 @@
# Stolen straight from ActiveSupport
class Proc #:nodoc:
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

View File

@ -1,239 +0,0 @@
require File.join(File.dirname(__FILE__), 'proc_extensions')
module Shoulda
class << self
attr_accessor :current_context
end
VERSION = '1.1.1'
# = Should statements
#
# Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
# contains all the normal code and assertions you're used to seeing, with the added benefit that
# they can be wrapped inside context blocks (see below).
#
# == Example:
#
# class UserTest << Test::Unit::TestCase
#
# def setup
# @user = User.new("John", "Doe")
# end
#
# should "return its full name"
# assert_equal 'John Doe', @user.full_name
# end
#
# end
#
# ...will produce the following test:
# * <tt>"test: User should return its full name. "</tt>
#
# Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class.
def should(name, &blk)
should_eventually(name) && return unless block_given?
if Shoulda.current_context
Shoulda.current_context.should(name, &blk)
else
context_name = self.name.gsub(/Test/, "")
context = Shoulda::Context.new(context_name, self) do
should(name, &blk)
end
context.build
end
end
# Just like should, but never runs, and instead prints an 'X' in the Test::Unit output.
def should_eventually(name, &blk)
context_name = self.name.gsub(/Test/, "")
context = Shoulda::Context.new(context_name, self) do
should_eventually(name, &blk)
end
context.build
end
# = Contexts
#
# A context block groups should statements under a common set of setup/teardown methods.
# Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
# and readability of your test code.
#
# A context block can contain setup, should, should_eventually, and teardown blocks.
#
# class UserTest << Test::Unit::TestCase
# 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>.
#
# Contexts may be nested. Nested contexts run their setup blocks from out to in before each
# should statement. They then run their teardown blocks from in to out after each should statement.
#
# class UserTest << Test::Unit::TestCase
# 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>
#
# <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt>
# tests</b>. This means you do not have to fully commit to the context/should syntax in a test file.
def context(name, &blk)
if Shoulda.current_context
Shoulda.current_context.context(name, &blk)
else
context = Shoulda::Context.new(name, self, &blk)
context.build
end
end
class Context # :nodoc:
attr_accessor :name # my name
attr_accessor :parent # may be another context, or the original test::unit class.
attr_accessor :subcontexts # array of contexts nested under myself
attr_accessor :setup_block # block given via a setup method
attr_accessor :teardown_block # block given via a teardown method
attr_accessor :shoulds # array of hashes representing the should statements
attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
def initialize(name, parent, &blk)
Shoulda.current_context = self
self.name = name
self.parent = parent
self.setup_block = nil
self.teardown_block = nil
self.shoulds = []
self.should_eventuallys = []
self.subcontexts = []
blk.bind(self).call
Shoulda.current_context = nil
end
def context(name, &blk)
subcontexts << Context.new(name, self, &blk)
Shoulda.current_context = self
end
def setup(&blk)
self.setup_block = blk
end
def teardown(&blk)
self.teardown_block = blk
end
def should(name, &blk)
self.shoulds << { :name => name, :block => blk }
end
def should_eventually(name, &blk)
self.should_eventuallys << { :name => name, :block => blk }
end
def full_name
parent_name = parent.full_name if am_subcontext?
return [parent_name, name].join(" ").strip
end
def am_subcontext?
parent.is_a?(self.class) # my parent is the same class as myself.
end
def test_unit_class
am_subcontext? ? parent.test_unit_class : parent
end
def create_test_from_should_hash(should)
test_name = ["test:", full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym
if test_unit_class.instance_methods.include?(test_name.to_s)
warn " * WARNING: '#{test_name}' is already defined"
end
context = self
test_unit_class.send(:define_method, test_name) do |*args|
begin
context.run_all_setup_blocks(self)
should[:block].bind(self).call
ensure
context.run_all_teardown_blocks(self)
end
end
end
def run_all_setup_blocks(binding)
self.parent.run_all_setup_blocks(binding) if am_subcontext?
setup_block.bind(binding).call if setup_block
end
def run_all_teardown_blocks(binding)
teardown_block.bind(binding).call if teardown_block
self.parent.run_all_teardown_blocks(binding) if am_subcontext?
end
def print_should_eventuallys
should_eventuallys.each do |should|
test_name = [full_name, "should", "#{should[:name]}. "].flatten.join(' ')
puts " * DEFERRED: " + test_name
end
subcontexts.each { |context| context.print_should_eventuallys }
end
def build
shoulds.each do |should|
create_test_from_should_hash(should)
end
subcontexts.each { |context| context.build }
print_should_eventuallys
end
def method_missing(method, *args, &blk)
test_unit_class.send(method, *args, &blk)
end
end
end
module Test # :nodoc: all
module Unit
class TestCase
extend Shoulda
end
end
end

View File

@ -64,10 +64,4 @@ class ContextTest < Test::Unit::TestCase # :nodoc:
end
end
should_eventually "should pass, since it's unimplemented" do
flunk "what?"
end
should_eventually "should not require a block when using should_eventually"
should "should pass without a block, as that causes it to piggyback to should_eventually"
end

View File

@ -0,0 +1,236 @@
require 'test/unit'
require 'rubygems'
require 'mocha'
class ShouldaTest < Test::Unit::TestCase # :nodoc:
should "be able to define a should statement outside of a context" do
assert true
end
should "see the name of my class as ShouldaTest" do
assert_equal "ShouldaTest", self.class.name
end
def self.should_see_class_methods
should "be able to see class methods" do
assert true
end
end
def self.should_see_a_context_block_like_a_Test_Unit_class
should "see a context block as a Test::Unit class" do
assert_equal "ShouldaTest", self.class.name
end
end
def self.should_see_blah
should "see @blah through a macro" do
assert @blah
end
end
def self.should_not_see_blah
should "not see @blah through a macro" do
assert_nil @blah
end
end
def self.should_be_able_to_make_context_macros(prefix = nil)
context "a macro" do
should "have the tests named correctly" do
assert_match(/^test: #{prefix}a macro should have the tests named correctly/, self.to_s)
end
end
end
context "Context" do
should_see_class_methods
should_see_a_context_block_like_a_Test_Unit_class
should_be_able_to_make_context_macros("Context ")
should "not define @blah" do
assert ! self.instance_variables.include?("@blah")
end
should_not_see_blah
should "be able to define a should statement" do
assert true
end
should "see the name of my class as ShouldaTest" do
assert_equal "ShouldaTest", self.class.name
end
context "with a subcontext" do
should_be_able_to_make_context_macros("Context with a subcontext ")
end
end
context "Context with setup block" do
setup do
@blah = "blah"
end
should "have @blah == 'blah'" do
assert_equal "blah", @blah
end
should_see_blah
should "have name set right" do
assert_match(/^test: Context with setup block/, self.to_s)
end
context "and a subcontext" do
setup do
@blah = "#{@blah} twice"
end
should "be named correctly" do
assert_match(/^test: Context with setup block and a subcontext should be named correctly/, self.to_s)
end
should "run the setup methods in order" do
assert_equal @blah, "blah twice"
end
should_see_blah
end
end
context "Another context with setup block" do
setup do
@blah = "foo"
end
should "have @blah == 'foo'" do
assert_equal "foo", @blah
end
should "have name set right" do
assert_match(/^test: Another context with setup block/, self.to_s)
end
should_see_blah
end
should_eventually "pass, since it's a should_eventually" do
flunk "what?"
end
should_eventually "should not require a block when using should_eventually"
should "should pass without a block, as that causes it to piggyback to should_eventually"
# Context creation and naming
def test_should_create_a_new_context
assert_nothing_raised do
Shoulda::Context.new("context name", self) do; end
end
end
def test_should_create_a_nested_context
assert_nothing_raised do
parent = Shoulda::Context.new("Parent", self) do; end
child = Shoulda::Context.new("Child", parent) do; end
end
end
def test_should_name_a_contexts_correctly
parent = Shoulda::Context.new("Parent", self) do; end
child = Shoulda::Context.new("Child", parent) do; end
grandchild = Shoulda::Context.new("GrandChild", child) do; end
assert_equal "Parent", parent.full_name
assert_equal "Parent Child", child.full_name
assert_equal "Parent Child GrandChild", grandchild.full_name
end
# Should statements
def test_should_have_should_hashes_when_given_should_statements
context = Shoulda::Context.new("name", self) do
should "be good" do; end
should "another" do; end
end
names = context.shoulds.map {|s| s[:name]}
assert_equal ["another", "be good"], names.sort
end
# setup and teardown
def test_should_capture_setup_and_teardown_blocks
context = Shoulda::Context.new("name", self) do
setup do; "setup"; end
teardown do; "teardown"; end
end
assert_equal "setup", context.setup_block.call
assert_equal "teardown", context.teardown_block.call
end
# building
def test_should_create_shoulda_test_for_each_should_on_build
context = Shoulda::Context.new("name", self) do
should "one" do; end
should "two" do; end
end
context.expects(:create_test_from_should_hash).with(has_entry(:name => "one"))
context.expects(:create_test_from_should_hash).with(has_entry(:name => "two"))
context.build
end
def test_should_create_test_methods_on_build
tu_class = Test::Unit::TestCase
context = Shoulda::Context.new("A Context", tu_class) do
should "define the test" do; end
end
tu_class.expects(:define_method).with(:"test: A Context should define the test. ")
context.build
end
def test_should_create_test_methods_on_build_when_subcontext
tu_class = Test::Unit::TestCase
context = Shoulda::Context.new("A Context", tu_class) do
context "with a child" do
should "define the test" do; end
end
end
tu_class.expects(:define_method).with(:"test: A Context with a child should define the test. ")
context.build
end
# Test::Unit integration
def test_should_create_a_new_context_and_build_it_on_Test_Unit_context
c = mock("context")
c.expects(:build)
Shoulda::Context.expects(:new).with("foo", kind_of(Class)).returns(c)
self.class.context "foo" do; end
end
def test_should_create_a_one_off_context_and_build_it_on_Test_Unit_should
s = mock("test")
Shoulda::Context.any_instance.expects(:should).with("rock").returns(s)
Shoulda::Context.any_instance.expects(:build)
self.class.should "rock" do; end
end
def test_should_define_a_test_on_should
s = mock("test")
Shoulda::Context.any_instance.expects(:should).with("rock").returns(s)
Shoulda::Context.any_instance.expects(:build)
self.class.should "rock" do; end
end
def test_should_create_a_one_off_context_and_build_it_on_Test_Unit_should_eventually
s = mock("test")
Shoulda::Context.any_instance.expects(:should_eventually).with("rock").returns(s)
Shoulda::Context.any_instance.expects(:build)
self.class.should_eventually "rock" do; end
end
end