diff --git a/Rakefile b/Rakefile
index 52c47ecf..4bb9a7ea 100644
--- a/Rakefile
+++ b/Rakefile
@@ -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
diff --git a/lib/shoulda.rb b/lib/shoulda.rb
index cd571df2..e581977e 100644
--- a/lib/shoulda.rb
+++ b/lib/shoulda.rb
@@ -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:
+ # * "test: User should return its full name. "
+ #
+ # Note: The part before should 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 "test: A User instance should return its full name. ".
+ #
+ # 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
+ # * "test: A User instance should return its full name. "
+ # * "test: A User instance with a profile should return true when sent :has_profile?. "
+ #
+ # Just like should statements, a context block can exist next to normal def test_the_old_way; end
+ # tests. 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
diff --git a/lib/shoulda/color.rb b/lib/shoulda/color.rb
index 1324dd7b..170ae2c1 100644
--- a/lib/shoulda/color.rb
+++ b/lib/shoulda/color.rb
@@ -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 color: true in
# your ~/.shoulda.conf file to enable.
diff --git a/lib/shoulda/context.rb b/lib/shoulda/context.rb
new file mode 100644
index 00000000..333458ea
--- /dev/null
+++ b/lib/shoulda/context.rb
@@ -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
+
diff --git a/lib/shoulda/extensions/proc.rb b/lib/shoulda/extensions/proc.rb
new file mode 100644
index 00000000..ec2c7d88
--- /dev/null
+++ b/lib/shoulda/extensions/proc.rb
@@ -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
diff --git a/lib/shoulda/gem/proc_extensions.rb b/lib/shoulda/gem/proc_extensions.rb
deleted file mode 100644
index 0d577df9..00000000
--- a/lib/shoulda/gem/proc_extensions.rb
+++ /dev/null
@@ -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
diff --git a/lib/shoulda/gem/shoulda.rb b/lib/shoulda/gem/shoulda.rb
deleted file mode 100644
index 19163089..00000000
--- a/lib/shoulda/gem/shoulda.rb
+++ /dev/null
@@ -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:
- # * "test: User should return its full name. "
- #
- # Note: The part before should 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 "test: A User instance should return its full name. ".
- #
- # 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
- # * "test: A User instance should return its full name. "
- # * "test: A User instance with a profile should return true when sent :has_profile?. "
- #
- # Just like should statements, a context block can exist next to normal def test_the_old_way; end
- # tests. 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
-
diff --git a/test/other/context_test.rb b/test/shoulda/context_test.rb
similarity index 85%
rename from test/other/context_test.rb
rename to test/shoulda/context_test.rb
index 2a17e249..2000e3d1 100644
--- a/test/other/context_test.rb
+++ b/test/shoulda/context_test.rb
@@ -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
diff --git a/test/other/helpers_test.rb b/test/shoulda/helpers_test.rb
similarity index 100%
rename from test/other/helpers_test.rb
rename to test/shoulda/helpers_test.rb
diff --git a/test/other/private_helpers_test.rb b/test/shoulda/private_helpers_test.rb
similarity index 100%
rename from test/other/private_helpers_test.rb
rename to test/shoulda/private_helpers_test.rb
diff --git a/test/shoulda/shoulda_test.rb b/test/shoulda/shoulda_test.rb
new file mode 100644
index 00000000..bd26e06a
--- /dev/null
+++ b/test/shoulda/shoulda_test.rb
@@ -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