Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Tagging 0.4.0 release.

  • Loading branch information...
commit 04b02f0f900f6882bb4e856d762e6675d2d323a9 1 parent 784295f
@floehopper floehopper authored
Showing with 3,911 additions and 0 deletions.
  1. +3 −0  trunk/COPYING
  2. +7 −0 trunk/MIT-LICENSE
  3. +35 −0 trunk/README
  4. +66 −0 trunk/RELEASE
  5. +126 −0 trunk/Rakefile
  6. +49 −0 trunk/TODO
  7. +36 −0 trunk/examples/misc.rb
  8. +26 −0 trunk/examples/mocha.rb
  9. +65 −0 trunk/examples/stubba.rb
  10. +4 −0 trunk/init.rb
  11. +19 −0 trunk/lib/mocha.rb
  12. +35 −0 trunk/lib/mocha/any_instance_method.rb
  13. +113 −0 trunk/lib/mocha/auto_verify.rb
  14. +35 −0 trunk/lib/mocha/central.rb
  15. +62 −0 trunk/lib/mocha/class_method.rb
  16. +295 −0 trunk/lib/mocha/expectation.rb
  17. +6 −0 trunk/lib/mocha/expectation_error.rb
  18. +27 −0 trunk/lib/mocha/infinite_range.rb
  19. +37 −0 trunk/lib/mocha/inspect.rb
  20. +8 −0 trunk/lib/mocha/instance_method.rb
  21. +7 −0 trunk/lib/mocha/metaclass.rb
  22. +20 −0 trunk/lib/mocha/mock.rb
  23. +122 −0 trunk/lib/mocha/mock_methods.rb
  24. +100 −0 trunk/lib/mocha/object.rb
  25. +28 −0 trunk/lib/mocha/pretty_parameters.rb
  26. +23 −0 trunk/lib/mocha/setup_and_teardown.rb
  27. +30 −0 trunk/lib/mocha/standalone.rb
  28. +49 −0 trunk/lib/mocha/test_case_adapter.rb
  29. +2 −0  trunk/lib/mocha_standalone.rb
  30. +2 −0  trunk/lib/stubba.rb
  31. +36 −0 trunk/test/active_record_test_case.rb
  32. +75 −0 trunk/test/all_tests.rb
  33. +34 −0 trunk/test/execution_point.rb
  34. +18 −0 trunk/test/method_definer.rb
  35. +124 −0 trunk/test/mocha/any_instance_method_test.rb
  36. +163 −0 trunk/test/mocha/auto_verify_test.rb
  37. +124 −0 trunk/test/mocha/central_test.rb
  38. +196 −0 trunk/test/mocha/class_method_test.rb
  39. +357 −0 trunk/test/mocha/expectation_test.rb
  40. +50 −0 trunk/test/mocha/infinite_range_test.rb
  41. +90 −0 trunk/test/mocha/inspect_test.rb
  42. +22 −0 trunk/test/mocha/metaclass_test.rb
  43. +235 −0 trunk/test/mocha/mock_methods_test.rb
  44. +84 −0 trunk/test/mocha/mock_test.rb
  45. +165 −0 trunk/test/mocha/object_test.rb
  46. +32 −0 trunk/test/mocha/pretty_parameters_test.rb
  47. +76 −0 trunk/test/mocha/setup_and_teardown_test.rb
  48. +98 −0 trunk/test/mocha_acceptance_test.rb
  49. +105 −0 trunk/test/mocha_test_result_integration_test.rb
  50. +110 −0 trunk/test/standalone_acceptance_test.rb
  51. +102 −0 trunk/test/stubba_acceptance_test.rb
  52. +89 −0 trunk/test/stubba_integration_test.rb
  53. +85 −0 trunk/test/stubba_test_result_integration_test.rb
  54. +4 −0 trunk/test/test_helper.rb
View
3  trunk/COPYING
@@ -0,0 +1,3 @@
+Copyright Revieworld Ltd. 2006
+
+You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt) or under the MIT license (see MIT-LICENSE file).
View
7 trunk/MIT-LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2006 Revieworld Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
35 trunk/README
@@ -0,0 +1,35 @@
+= Mocha
+
+Mocha is a library for mocking and stubbing using a syntax like that of JMock[http://www.jmock.org], and SchMock[http://rubyforge.org/projects/schmock]. Most commonly Mocha is used in conjunction with Test::Unit[http://www.ruby-doc.org/core/classes/Test/Unit.html], but it can be used in other contexts.
+
+One of its main advantages is that it allows you to mock and stub methods on _real_ (non-mock) classes and instances. You can for example stub ActiveRecord[http://api.rubyonrails.com/classes/ActiveRecord/Base.html] instance methods like +create+, +save+, +destroy+ and even class methods like +find+ to avoid hitting the database in unit tests.
+
+Mocha provides a unified, simple and readable syntax for both traditional mocking and for mocking with _real_ objects.
+
+Mocha has been harvested from projects at Reevoo[http://www.reevoo.com] by me (James[http://blog.floehopper.org]) and my colleagues Ben[http://www.reevoo.com/blogs/bengriffiths], Chris[http://blog.seagul.co.uk] and Paul[http://po-ru.com]. Mocha is in use on real-world Rails[http://www.rubyonrails.org] projects.
+
+== Download and Installation
+
+Install the gem with the following command...
+
+ $ gem install mocha
+
+Or install the Rails[http://www.rubyonrails.org] plugin...
+
+ $ script/plugin install svn://rubyforge.org/var/svn/mocha/trunk
+
+Or download Mocha from here - http://rubyforge.org/projects/mocha
+
+== Examples
+
+* Quick Start - {Usage Examples}[link:examples/misc.html]
+* Traditional mocking - {Star Trek Example}[link:examples/mocha.html]
+* Setting expectations on real classes - {Order Example}[link:examples/stubba.html]
+* More examples on {Floehopper's Blog}[http://blog.floehopper.org]
+* {Mailing List Archives}[http://rubyforge.org/pipermail/mocha-developer/]
+
+== License
+
+Copyright Revieworld Ltd. 2006
+
+You may use, copy and redistribute this library under the same terms as {Ruby itself}[http://www.ruby-lang.org/en/LICENSE.txt] or under the {MIT license}[http://mocha.rubyforge.org/files/MIT-LICENSE.html].
View
66 trunk/RELEASE
@@ -0,0 +1,66 @@
+= 0.3.0
+
+* Rails plugin.
+* Auto-verify for expectations on concrete classes.
+* Include each expectation verification in the test result assertion count.
+* Filter out noise from assertion backtraces.
+* Point assertion backtrace to line where failing expectation was created.
+* New yields method for expectations.
+* Create stubs which stub all method calls.
+* Mocks now respond_to? expected methods.
+
+= 0.2.1
+
+* Rename MochaAcceptanceTest::Rover#move method to avoid conflict with Rake (in Ruby 1.8.4 only?)
+
+= 0.2.0
+
+* Small change to SetupAndTeardown#teardown_stubs suggested by Luke Redpath (http://www.lukeredpath.co.uk) to allow use of Stubba with RSpec (http://rspec.rubyforge.org).
+* Reorganized directory structure and extracted addition of setup and teardown methods into SmartTestCase mini-library.
+* Addition of auto-verify for Mocha (but not Stubba). This means there is more significance in the choice of expects or stubs in that any expects on a mock will automatically get verified.
+
+So instead of...
+
+ wotsit = Mocha.new
+ wotsit.expects(:thingummy).with(5).returns(10)
+ doobrey = Doobrey.new(wotsit)
+ doobrey.hoojamaflip
+ wotsit.verify
+
+you need to do...
+
+ wotsit = mock()
+ wotsit.expects(:thingummy).with(5).returns(10)
+ doobrey = Doobrey.new(wotsit)
+ doobrey.hoojamaflip
+ # no need to verify
+
+There are also shortcuts as follows...
+
+instead of...
+
+ wotsit = Mocha.new
+ wotsit.expects(:thingummy).returns(10)
+ wotsit.expects(:summat).returns(25)
+
+you can have...
+
+ wotsit = mock(:thingummy => 5, :summat => 25)
+
+and instead of...
+
+ wotsit = Mocha.new
+ wotsit.stubs(:thingummy).returns(10)
+ wotsit.stubs(:summat).returns(25)
+
+you can have...
+
+ wotsit = stub(:thingummy => 5, :summat => 25)
+
+= 0.1.2
+
+* Minor tweaks
+
+= 0.1.1
+
+* Initial release.
View
126 trunk/Rakefile
@@ -0,0 +1,126 @@
+require 'rubygems'
+require 'rake/rdoctask'
+require 'rake/gempackagetask'
+require 'rake/contrib/sshpublisher'
+
+module Mocha
+ VERSION = "0.4.0"
+end
+
+desc "Default task is currently to run all tests"
+task :default => :test_all
+
+desc "Run all tests"
+task :test_all do
+ $: << "#{File.dirname(__FILE__)}/test"
+ require 'test/all_tests'
+end
+
+desc 'Generate RDoc'
+Rake::RDocTask.new do |task|
+ task.main = 'README'
+ task.title = 'Mocha'
+ task.rdoc_dir = 'doc'
+ task.template = "html_with_google_analytics"
+ task.options << "--line-numbers" << "--inline-source"
+ task.rdoc_files.include('README', 'RELEASE', 'COPYING', 'MIT-LICENSE', 'agiledox.txt', 'lib/mocha/auto_verify.rb', 'lib/mocha/mock_methods.rb', 'lib/mocha/expectation.rb', 'lib/mocha/object.rb')
+end
+task :rdoc => :examples
+
+desc "Upload RDoc to RubyForge"
+task :publish_rdoc => [:rdoc, :examples] do
+ Rake::SshDirPublisher.new("jamesmead@rubyforge.org", "/var/www/gforge-projects/mocha", "doc").upload
+end
+
+desc "Generate agiledox-like documentation for tests"
+file 'agiledox.txt' do
+ File.open('agiledox.txt', 'w') do |output|
+ tests = FileList['test/**/*_test.rb']
+ tests.each do |file|
+ m = %r".*/([^/].*)_test.rb".match(file)
+ output << m[1]+" should:\n"
+ test_definitions = File::readlines(file).select {|line| line =~ /.*def test.*/}
+ test_definitions.sort.each do |definition|
+ m = %r"test_(should_)?(.*)".match(definition)
+ output << " - "+m[2].gsub(/_/," ") << "\n"
+ end
+ end
+ end
+end
+
+desc "Convert example ruby files to syntax-highlighted html"
+task :examples do
+ require 'coderay'
+ mkdir_p 'doc/examples'
+ File.open('doc/examples/coderay.css', 'w') do |output|
+ output << CodeRay::Encoders[:html]::CSS.new.stylesheet
+ end
+ ['mocha', 'stubba', 'misc'].each do |filename|
+ File.open("doc/examples/#{filename}.html", 'w') do |file|
+ file << "<html>"
+ file << "<head>"
+ file << %q(<link rel="stylesheet" media="screen" href="coderay.css" type="text/css">)
+ file << "</head>"
+ file << "<body>"
+ file << CodeRay.scan_file("examples/#{filename}.rb").html.div
+ file << "</body>"
+ file << "</html>"
+ end
+ end
+end
+
+Gem::manage_gems
+
+specification = Gem::Specification.new do |s|
+ s.name = "mocha"
+ s.summary = "Mocking and stubbing library"
+ s.version = Mocha::VERSION
+ s.author = 'James Mead'
+ s.description = <<-EOF
+ Mocking and stubbing library with JMock/SchMock syntax, which allows mocking and stubbing of methods on real (non-mock) classes.
+ EOF
+ s.email = 'mocha-developer@rubyforge.org'
+ s.homepage = 'http://mocha.rubyforge.org'
+ s.rubyforge_project = 'mocha'
+
+ s.has_rdoc = true
+ s.extra_rdoc_files = ['README', 'COPYING']
+ s.rdoc_options << '--title' << 'Mocha' << '--main' << 'README' << '--line-numbers'
+
+ s.autorequire = 'mocha'
+ s.files = FileList['{lib,test,examples}/**/*.rb', '[A-Z]*'].exclude('TODO').to_a
+ s.test_file = "test/all_tests.rb"
+end
+
+Rake::GemPackageTask.new(specification) do |package|
+ package.need_zip = true
+ package.need_tar = true
+end
+
+task :verify_user do
+ raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
+end
+
+task :verify_password do
+ raise "RUBYFORGE_PASSWORD environment variable not set!" unless ENV['RUBYFORGE_PASSWORD']
+end
+
+desc "Publish package files on RubyForge."
+task :publish_packages => [:verify_user, :verify_password, :package] do
+ require 'meta_project'
+ require 'rake/contrib/xforge'
+ release_files = FileList[
+ "pkg/mocha-#{Mocha::VERSION}.gem",
+ "pkg/mocha-#{Mocha::VERSION}.tgz",
+ "pkg/mocha-#{Mocha::VERSION}.zip"
+ ]
+
+ Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new('mocha')) do |release|
+ release.user_name = ENV['RUBYFORGE_USER']
+ release.password = ENV['RUBYFORGE_PASSWORD']
+ release.files = release_files.to_a
+ release.release_name = "Mocha #{Mocha::VERSION}"
+ release.release_changes = ''
+ release.release_notes = ''
+ end
+end
View
49 trunk/TODO
@@ -0,0 +1,49 @@
+=> restrict partial mocks to existing public methods?
+- allow specification of class to be mocked, so we can check for existence of methods
+- investigate where/why check for existence of stubbed method has gone.
+- allow stubbing of private/protected methods? - force_stub?
+
+=> multiple expectations for same method
+- look at JMock for examples of when multiple expectations match
+- increment actual count for ANY expectation that matches - see email from Bryan Helmkamp
+- think about allowing a stubbing expectation to be converted to an asserting expectation and vice versa
+- fail fast if expectation count exceeded during test...? c.f. JMock?
+- think about behaviour when more than one expectation/stubbed method match c.f. JMock?
+
+=> reduce footprint of mocha in terms of visible methods
+- do away with __is_a__ method if possible - Proc param may no longer be needed
+- add similar test to test_should_be_able_to_mock_standard_object_methods for partial mocks - rename non-public method with underscores e.g. mocha, reset_mocha, stubba_method, stubba_object, etc.
+- reduce number of methods added to Object, Class etc to bare minimum
+- reduce number of methods excluded from undef in mock_methods (maybe use blank_slate as mocha parent class to allow mocking of standard object methods?)
+- perhaps only add methods to particular class at point where expects or stubs gets called
+- provide some means to un-stubba an object - ideally should restore any methods with same names as stubba methods c.f. dealing with expectations for methods called :expects, :stubs, etc.
+- use Object#inspect(:mocha) or Object#__inspect__ instead of Object#mocha_inspect?
+
+=> improve design
+- use builder object c.f. JMock
+- possibly simplify by making mock the same as a partial mock of Object
+- provide test::unit agnostic api - mockery/context type objects on which the user must call setup/teardown?
+
+=> release management
+- why does gem rdoc include all source files?
+- tags for repos
+- automate releases cf rspec
+
+=> documentation
+- read and improve tutorial emailed by user
+
+=> naming
+- default mock names to mock1, mock2, etc or something similar
+- stubba mocks should named according to parent stubbee
+
+=> improve tests
+- use real activerecord as svn external to for testing instead of active_record_test_case
+- test for setting expectations on class methods (and instance methods?) from within TestCase#setup
+
+=> possible extra functionality
+- add multiyield - see email from Jay Fields
+- allow hash parameter for stubs and expects methods particularly for stubba
+- more jmock style stuff - e.g. labels/required order, more sophisticated param matching?
+- maybe allow unstubbing of a specific method from within a test...?
+- oomatron type ideas
+- should all instances share expectations for any_instance or should each instance have their own - in which case how do we provide access to the instances
View
36 trunk/examples/misc.rb
@@ -0,0 +1,36 @@
+# Mocking a class method
+
+ product = Product.new
+ Product.expects(:find).with(1).returns(product)
+ assert_equal product, Product.find(1)
+
+# Mocking an instance method on a real object
+
+ product = Product.new
+ product.expects(:save).returns(true)
+ assert product.save
+
+# Stubbing instance methods on real object
+
+ prices = [stub(:pence => 1000), stub(:pence => 2000)]
+ product = Product.new
+ product.stubs(:prices).returns(prices)
+ assert_equal [1000, 2000], product.prices.collect {|p| p.pence}
+
+# Stubbing an instance method on all instances of a class
+
+ Product.any_instance.stubs(:name).returns('stubbed_name')
+ product = Product.new
+ assert_equal 'stubbed_name', product.name
+
+# Traditional mocking
+
+ object = mock()
+ object.expects(:expected_method).with(:p1, :p2).returns(:result)
+ assert_equal :result, object.expected_method(:p1, :p2)
+
+# Shortcuts
+
+ object = stub(:method1 => :result1, :method2 => :result2)
+ assert_equal :result1, object.method1
+ assert_equal :result2, object.method2
View
26 trunk/examples/mocha.rb
@@ -0,0 +1,26 @@
+class Enterprise
+
+ def initialize(dilithium)
+ @dilithium = dilithium
+ end
+
+ def go(warp_factor)
+ warp_factor.times { @dilithium.nuke(:anti_matter) }
+ end
+
+end
+
+require 'test/unit'
+require 'rubygems'
+require 'mocha'
+
+class EnterpriseTest < Test::Unit::TestCase
+
+ def test_should_boldly_go
+ dilithium = mock()
+ dilithium.expects(:nuke).with(:anti_matter).at_least_once # auto-verified at end of test
+ enterprise = Enterprise.new(dilithium)
+ enterprise.go(2)
+ end
+
+end
View
65 trunk/examples/stubba.rb
@@ -0,0 +1,65 @@
+class Order
+
+ attr_accessor :shipped_on
+
+ def total_cost
+ line_items.inject(0) { |total, line_item| total + line_item.price } + shipping_cost
+ end
+
+ def total_weight
+ line_items.inject(0) { |total, line_item| total + line_item.weight }
+ end
+
+ def shipping_cost
+ total_weight * 5 + 10
+ end
+
+ class << self
+
+ def find_all
+ # Database.connection.execute('select * from orders...
+ end
+
+ def number_shipped_since(date)
+ find_all.select { |order| order.shipped_on > date }.size
+ end
+
+ def unshipped_value
+ find_all.inject(0) { |total, order| order.shipped_on ? total : total + order.total_cost }
+ end
+
+ end
+
+end
+
+require 'test/unit'
+require 'rubygems'
+require 'stubba'
+
+class OrderTest < Test::Unit::TestCase
+
+ # illustrates stubbing instance method
+ def test_should_calculate_shipping_cost_based_on_total_weight
+ order = Order.new
+ order.stubs(:total_weight).returns(10)
+ assert_equal 60, order.shipping_cost
+ end
+
+ # illustrates stubbing class method
+ def test_should_count_number_of_orders_shipped_after_specified_date
+ now = Time.now; week_in_secs = 7 * 24 * 60 * 60
+ order_1 = Order.new; order_1.shipped_on = now - 1 * week_in_secs
+ order_2 = Order.new; order_2.shipped_on = now - 3 * week_in_secs
+ Order.stubs(:find_all).returns([order_1, order_2])
+ assert_equal 1, Order.number_shipped_since(now - 2 * week_in_secs)
+ end
+
+ # illustrates stubbing instance method for all instances of a class
+ def test_should_calculate_value_of_unshipped_orders
+ Order.stubs(:find_all).returns([Order.new, Order.new, Order.new])
+ Order.any_instance.stubs(:shipped_on).returns(nil)
+ Order.any_instance.stubs(:total_cost).returns(10)
+ assert_equal 30, Order.unshipped_value
+ end
+
+end
View
4 trunk/init.rb
@@ -0,0 +1,4 @@
+if ENV['RAILS_ENV'] == 'test' then
+ require 'test_help'
+ require 'mocha'
+end
View
19 trunk/lib/mocha.rb
@@ -0,0 +1,19 @@
+require 'mocha_standalone'
+require 'mocha/test_case_adapter'
+
+require 'test/unit/testcase'
+
+module Test
+
+ module Unit
+
+ class TestCase
+
+ include Mocha::Standalone
+ include Mocha::TestCaseAdapter
+
+ end
+
+ end
+
+end
View
35 trunk/lib/mocha/any_instance_method.rb
@@ -0,0 +1,35 @@
+require 'mocha/class_method'
+
+module Mocha
+
+ class AnyInstanceMethod < ClassMethod
+
+ def unstub
+ remove_new_method
+ restore_original_method
+ stubbee.any_instance.reset_mocha
+ end
+
+ def mock
+ stubbee.any_instance.mocha
+ end
+
+ def hide_original_method
+ stubbee.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.method_defined?(method)
+ end
+
+ def define_new_method
+ stubbee.class_eval "def #{method}(*args, &block); self.class.any_instance.mocha.method_missing(:#{method}, *args, &block); end"
+ end
+
+ def remove_new_method
+ stubbee.class_eval "remove_method :#{method}"
+ end
+
+ def restore_original_method
+ stubbee.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.method_defined?(hidden_method)
+ end
+
+ end
+
+end
View
113 trunk/lib/mocha/auto_verify.rb
@@ -0,0 +1,113 @@
+require 'mocha/mock'
+
+# Methods added to TestCase allowing creation of mock objects.
+#
+# Mocks created this way will have their expectations automatically verified at the end of the test.
+#
+# See Mocha::MockMethods for methods on mock objects.
+module Mocha
+
+ module AutoVerify
+
+ def mocks # :nodoc:
+ @mocks ||= []
+ end
+
+ def reset_mocks # :nodoc:
+ @mocks = nil
+ end
+
+ # :call-seq: mock(name) -> mock object
+ # mock(expected_methods = {}) -> mock object
+ # mock(name, expected_methods = {}) -> mock object
+ #
+ # Creates a mock object.
+ #
+ # +name+ is a +String+ identifier for the mock object.
+ #
+ # +expected_methods+ is a +Hash+ with expected method name symbols as keys and corresponding return values as values.
+ #
+ # Note that (contrary to expectations set up by #stub) these expectations <b>must</b> be fulfilled during the test.
+ # def test_product
+ # product = mock('ipod_product', :manufacturer => 'ipod', :price => 100)
+ # assert_equal 'ipod', product.manufacturer
+ # assert_equal 100, product.price
+ # # an error will be raised unless both Product#manufacturer and Product#price have been called
+ # end
+ def mock(*args)
+ name, expectations = name_and_expectations_from_args(args)
+ build_mock_with_expectations(:expects, expectations, name)
+ end
+
+ # :call-seq: stub(name) -> mock object
+ # stub(stubbed_methods = {}) -> mock object
+ # stub(name, stubbed_methods = {}) -> mock object
+ #
+ # Creates a mock object.
+ #
+ # +name+ is a +String+ identifier for the mock object.
+ #
+ # +stubbed_methods+ is a +Hash+ with stubbed method name symbols as keys and corresponding return values as values.
+ #
+ # Note that (contrary to expectations set up by #mock) these expectations <b>need not</b> be fulfilled during the test.
+ # def test_product
+ # product = stub('ipod_product', :manufacturer => 'ipod', :price => 100)
+ # assert_equal 'ipod', product.manufacturer
+ # assert_equal 100, product.price
+ # # an error will not be raised even if Product#manufacturer and Product#price have not been called
+ # end
+ def stub(*args)
+ name, expectations = name_and_expectations_from_args(args)
+ build_mock_with_expectations(:stubs, expectations, name)
+ end
+
+ # :call-seq: stub_everything(name) -> mock object
+ # stub_everything(stubbed_methods = {}) -> mock object
+ # stub_everything(name, stubbed_methods = {}) -> mock object
+ #
+ # Creates a mock object that accepts calls to any method.
+ #
+ # By default it will return +nil+ for any method call.
+ #
+ # +name+ and +stubbed_methods+ work in the same way as for #stub.
+ # def test_product
+ # product = stub_everything('ipod_product', :price => 100)
+ # assert_nil product.manufacturer
+ # assert_nil product.any_old_method
+ # assert_equal 100, product.price
+ # end
+ def stub_everything(*args)
+ name, expectations = name_and_expectations_from_args(args)
+ build_mock_with_expectations(:stub_everything, expectations, name)
+ end
+
+ def verify_mocks # :nodoc:
+ mocks.each { |mock| mock.verify { yield if block_given? } }
+ end
+
+ def teardown_mocks # :nodoc:
+ reset_mocks
+ end
+
+ def build_mock_with_expectations(expectation_type = :expects, expectations = {}, name = nil) # :nodoc:
+ stub_everything = (expectation_type == :stub_everything)
+ expectation_type = :stubs if expectation_type == :stub_everything
+ mock = Mocha::Mock.new(stub_everything, name)
+ expectations.each do |method, result|
+ mock.__send__(expectation_type, method).returns(result)
+ end
+ mocks << mock
+ mock
+ end
+
+ private
+
+ def name_and_expectations_from_args(args) # :nodoc:
+ name = args.first.is_a?(String) ? args.delete_at(0) : nil
+ expectations = args.first || {}
+ [name, expectations]
+ end
+
+ end
+
+end
View
35 trunk/lib/mocha/central.rb
@@ -0,0 +1,35 @@
+module Mocha
+
+ class Central
+
+ attr_accessor :stubba_methods
+
+ def initialize
+ self.stubba_methods = []
+ end
+
+ def stub(method)
+ unless stubba_methods.include?(method)
+ method.stub
+ stubba_methods.push method
+ end
+ end
+
+ def verify_all(&block)
+ unique_mocks.each { |mock| mock.verify(&block) }
+ end
+
+ def unique_mocks
+ stubba_methods.inject({}) { |mocks, method| mocks[method.mock.__id__] = method.mock; mocks }.values
+ end
+
+ def unstub_all
+ while stubba_methods.size > 0
+ method = stubba_methods.pop
+ method.unstub
+ end
+ end
+
+ end
+
+end
View
62 trunk/lib/mocha/class_method.rb
@@ -0,0 +1,62 @@
+require 'mocha/metaclass'
+
+module Mocha
+
+ class ClassMethod
+
+ attr_reader :stubbee, :method
+
+ def initialize(stubbee, method)
+ @stubbee, @method = stubbee, method
+ end
+
+ def stub
+ hide_original_method
+ define_new_method
+ end
+
+ def unstub
+ remove_new_method
+ restore_original_method
+ stubbee.reset_mocha
+ end
+
+ def mock
+ stubbee.mocha
+ end
+
+ def hide_original_method
+ stubbee.__metaclass__.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.__metaclass__.method_defined?(method)
+ end
+
+ def define_new_method
+ stubbee.__metaclass__.class_eval "def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end"
+ end
+
+ def remove_new_method
+ stubbee.__metaclass__.class_eval "remove_method :#{method}"
+ end
+
+ def restore_original_method
+ stubbee.__metaclass__.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.__metaclass__.method_defined?(hidden_method)
+ end
+
+ def hidden_method
+ method_name = method.to_s.gsub(/\W/) {|s| "_substituted_character_#{s[0]}_" }
+ "__stubba__#{method_name}__stubba__"
+ end
+
+ def eql?(other)
+ return false unless (other.class == self.class)
+ (stubbee == other.stubbee) and (method == other.method)
+ end
+
+ alias_method :==, :eql?
+
+ def to_s
+ "#{stubbee}.#{method}"
+ end
+
+ end
+
+end
View
295 trunk/lib/mocha/expectation.rb
@@ -0,0 +1,295 @@
+require 'mocha/infinite_range'
+require 'mocha/pretty_parameters'
+require 'mocha/expectation_error'
+
+class Object
+
+ alias_method :__is_a__, :is_a?
+
+end
+
+module Mocha
+ # Methods on expectations returned from Mocha::MockMethods#expects and Mocha::MockMethods#stubs
+ class Expectation
+
+ # :stopdoc:
+
+ class InvalidExpectation < Exception; end
+
+ class AlwaysEqual
+ def ==(other)
+ true
+ end
+ end
+
+ attr_reader :method_name, :backtrace
+
+ def initialize(mock, method_name, backtrace = nil)
+ @mock, @method_name = mock, method_name
+ @count = 1
+ @parameters, @parameter_block = AlwaysEqual.new, nil
+ @invoked, @return_value = 0, nil
+ @backtrace = backtrace || caller
+ @yield = nil
+ end
+
+ def yield?
+ @yield
+ end
+
+ def match?(method_name, *arguments)
+ if @parameter_block then
+ @parameter_block.call(*arguments)
+ else
+ (@method_name == method_name) and (@parameters == arguments)
+ end
+ end
+
+ # :startdoc:
+
+ # :call-seq: times(range) -> expectation
+ #
+ # Modifies expectation so that the number of calls to the expected method must be within a specific +range+.
+ #
+ # +range+ can be specified as an exact integer or as a range of integers
+ # object = mock()
+ # object.expects(:expected_method).times(3)
+ # 3.times { object.expected_method } # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).times(3)
+ # 2.times { object.expected_method } # => verify fails
+ #
+ # object = mock()
+ # object.expects(:expected_method).times(2..4)
+ # 3.times { object.expected_method } # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).times(2..4)
+ # object.expected_method # => verify fails
+ def times(range)
+ @count = range
+ self
+ end
+
+ # :call-seq: never -> expectation
+ #
+ # Modifies expectation so that the expected method must never be called.
+ # object = mock()
+ # object.expects(:expected_method).never
+ # object.expected_method # => verify fails
+ #
+ # object = mock()
+ # object.expects(:expected_method).never
+ # object.expected_method # => verify succeeds
+ def never
+ times(0)
+ self
+ end
+
+ # :call-seq: at_least(minimum_number_of_times) -> expectation
+ #
+ # Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
+ # object = mock()
+ # object.expects(:expected_method).at_least(2)
+ # 3.times { object.expected_method } # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).at_least(2)
+ # object.expected_method # => verify fails
+ def at_least(minimum_number_of_times)
+ times(Range.at_least(minimum_number_of_times))
+ self
+ end
+
+ # :call-seq: at_least_once() -> expectation
+ #
+ # Modifies expectation so that the expected method must be called at least once.
+ # object = mock()
+ # object.expects(:expected_method).at_least_once
+ # object.expected_method # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).at_least_once
+ # # => verify fails
+ def at_least_once()
+ at_least(1)
+ self
+ end
+
+ # :call-seq: at_most(maximum_number_of_times) -> expectation
+ #
+ # Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
+ # object = mock()
+ # object.expects(:expected_method).at_most(2)
+ # 2.times { object.expected_method } # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).at_most(2)
+ # 3.times { object.expected_method } # => verify fails
+ def at_most(maximum_number_of_times)
+ times(Range.at_most(maximum_number_of_times))
+ self
+ end
+
+ # :call-seq: at_most_once() -> expectation
+ #
+ # Modifies expectation so that the expected method must be called at most once.
+ # object = mock()
+ # object.expects(:expected_method).at_most_once
+ # object.expected_method # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).at_most_once
+ # 2.times { object.expected_method } # => verify fails
+ def at_most_once()
+ at_most(1)
+ self
+ end
+
+ # :call-seq: with(*arguments, &parameter_block) -> expectation
+ #
+ # Modifies expectation so that the expected method must be called with specified +arguments+.
+ # object = mock()
+ # object.expects(:expected_method).with(:param1, :param2)
+ # object.expected_method(:param1, :param2) # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).with(:param1, :param2)
+ # object.expected_method(:param3) # => verify fails
+ # If a +parameter_block+ is given, the block is called with the parameters passed to the expected method.
+ # The expectation is matched if the block evaluates to +true+.
+ # object = mock()
+ # object.expects(:expected_method).with() { |value| value % 4 == 0 }
+ # object.expected_method(16) # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).with() { |value| value % 4 == 0 }
+ # object.expected_method(17) # => verify fails
+ def with(*arguments, &parameter_block)
+ @parameters, @parameter_block = arguments, parameter_block
+ class << @parameters; def to_s; join(', '); end; end
+ self
+ end
+
+ # :call-seq: yields(*parameters) -> expectation
+ #
+ # Modifies expectation so that when the expected method is called, it yields with the specified +parameters+.
+ # object = mock()
+ # object.expects(:expected_method).yields('result')
+ # yielded_value = nil
+ # object.expected_method { |value| yielded_value = value }
+ # yielded_value # => 'result'
+ def yields(*parameters)
+ @yield = true
+ @parameters_to_yield = parameters
+ self
+ end
+
+ # :call-seq: returns(value) -> expectation
+ # :call-seq: returns(*values) -> expectation
+ #
+ # Modifies expectation so that when the expected method is called, it returns the specified +value+.
+ # object = mock()
+ # object.stubs(:stubbed_method).returns('result')
+ # object.stubbed_method # => 'result'
+ # object.stubbed_method # => 'result'
+ # If multiple +values+ are given, these are returned in turn on consecutive calls to the method.
+ # object = mock()
+ # object.stubs(:stubbed_method).returns(1, 2)
+ # object.stubbed_method # => 1
+ # object.stubbed_method # => 2
+ # If +value+ is a Proc, then expected method will return result of calling Proc.
+ # object = mock()
+ # object.stubs(:stubbed_method).returns(lambda { rand(100) })
+ # object.stubbed_method # => 41
+ # object.stubbed_method # => 77
+ def returns(*values)
+ @return_value = (values.size > 1) ? lambda { values.shift } : @return_value = values.first
+ self
+ end
+
+ # :call-seq: raises(exception = RuntimeError, message = nil) -> expectation
+ #
+ # Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+.
+ # object = mock()
+ # object.expects(:expected_method).raises(Exception, 'message')
+ # object.expected_method # => raises exception of class Exception and with message 'message'
+ def raises(exception = RuntimeError, message = nil)
+ @return_value = message ? lambda { raise exception, message } : lambda { raise exception }
+ self
+ end
+
+ # :stopdoc:
+
+ def invoke
+ @invoked += 1
+ yield(*@parameters_to_yield) if yield? and block_given?
+ @return_value.__is_a__(Proc) ? @return_value.call : @return_value
+ end
+
+ def verify
+ yield(self) if block_given?
+ unless (@count === @invoked) then
+ error = ExpectationError.new(error_message(@count, @invoked))
+ error.set_backtrace(filtered_backtrace)
+ raise error
+ end
+ end
+
+ def mocha_lib_directory
+ File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR
+ end
+
+ def filtered_backtrace
+ backtrace.reject { |location| Regexp.new(mocha_lib_directory).match(File.expand_path(location)) }
+ end
+
+ def method_signature
+ return "#{method_name}" if @parameters.__is_a__(AlwaysEqual)
+ "#{@method_name}(#{PrettyParameters.new(@parameters).pretty})"
+ end
+
+ def error_message(expected_count, actual_count)
+ "#{@mock.mocha_inspect}.#{method_signature} - expected calls: #{expected_count}, actual calls: #{actual_count}"
+ end
+
+ # :startdoc:
+
+ end
+
+ # :stopdoc:
+
+ class Stub < Expectation
+
+ def verify
+ true
+ end
+
+ end
+
+ class MissingExpectation < Expectation
+
+ def initialize(mock, method_name)
+ super
+ @invoked = true
+ end
+
+ def verify
+ msg = error_message(0, 1)
+ similar_expectations_list = similar_expectations.collect { |expectation| expectation.method_signature }.join("\n")
+ msg << "\nSimilar expectations:\n#{similar_expectations_list}" unless similar_expectations.empty?
+ error = ExpectationError.new(msg)
+ error.set_backtrace(filtered_backtrace)
+ raise error if @invoked
+ end
+
+ def similar_expectations
+ @mock.expectations.select { |expectation| expectation.method_name == self.method_name }
+ end
+
+ end
+
+ # :startdoc:
+
+end
View
6 trunk/lib/mocha/expectation_error.rb
@@ -0,0 +1,6 @@
+module Mocha
+
+ class ExpectationError < StandardError
+ end
+
+end
View
27 trunk/lib/mocha/infinite_range.rb
@@ -0,0 +1,27 @@
+class Range
+
+ def self.at_least(minimum_value)
+ Range.new(minimum_value, infinite)
+ end
+
+ def self.at_most(maximum_value)
+ Range.new(-infinite, maximum_value, false)
+ end
+
+ def self.infinite
+ 1/0.0
+ end
+
+ alias_method :__to_s__, :to_s
+
+ def to_s
+ if first.to_f.infinite? then
+ return "at most #{last}"
+ elsif last.to_f.infinite? then
+ return "at least #{first}"
+ else
+ __to_s__
+ end
+ end
+
+end
View
37 trunk/lib/mocha/inspect.rb
@@ -0,0 +1,37 @@
+require 'date'
+
+class Object
+ def mocha_inspect
+ inspect =~ /#</ ? "#<#{self.class}:0x#{self.__id__.to_s(16)}>" : inspect
+ end
+end
+
+class String
+ def mocha_inspect
+ inspect.gsub(/\"/, "'")
+ end
+end
+
+class Array
+ def mocha_inspect
+ "[#{collect { |member| member.mocha_inspect }.join(', ')}]"
+ end
+end
+
+class Hash
+ def mocha_inspect
+ "{#{collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')}}"
+ end
+end
+
+class Time
+ def mocha_inspect
+ "#{inspect} (#{to_f} secs)"
+ end
+end
+
+class Date
+ def mocha_inspect
+ to_s
+ end
+end
View
8 trunk/lib/mocha/instance_method.rb
@@ -0,0 +1,8 @@
+require 'mocha/class_method'
+
+module Mocha
+
+ class InstanceMethod < ClassMethod
+ end
+
+end
View
7 trunk/lib/mocha/metaclass.rb
@@ -0,0 +1,7 @@
+class Object
+
+ def __metaclass__
+ class << self; self; end
+ end
+
+end
View
20 trunk/lib/mocha/mock.rb
@@ -0,0 +1,20 @@
+require 'mocha/mock_methods'
+
+module Mocha
+
+ class Mock
+
+ include MockMethods
+
+ def initialize(stub_everything = false, name = nil)
+ @stub_everything = stub_everything
+ @mock_name = name
+ end
+
+ def mocha_inspect
+ @mock_name ? "#<Mock:#{@mock_name}>" : "#<Mock:0x#{__id__.to_s(16)}>"
+ end
+
+ end
+
+end
View
122 trunk/lib/mocha/mock_methods.rb
@@ -0,0 +1,122 @@
+require 'mocha/expectation'
+require 'mocha/metaclass'
+
+module Mocha
+ # Methods added to mock objects.
+ # These methods all return an expectation which can be further modified by methods on Mocha::Expectation.
+ module MockMethods
+
+ # :stopdoc:
+
+ attr_reader :stub_everything
+
+ def expectations
+ @expectations ||= []
+ end
+
+ # :startdoc:
+
+ # :call-seq: expects(method_name) -> expectation
+ # expects(method_names) -> last expectation
+ #
+ # Adds an expectation that a method identified by +method_name+ symbol must be called exactly once with any parameters.
+ # Returns the new expectation which can be further modified by methods on Mocha::Expectation.
+ # object = mock()
+ # object.expects(:method1)
+ # object.method1
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method1)
+ # # error raised, because method1 not called exactly once
+ # If +method_names+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
+ # object = mock()
+ # object.expects(:method1 => :result1, :method2 => :result2)
+ #
+ # # exactly equivalent to
+ #
+ # object = mock()
+ # object.expects(:method1).returns(:result1)
+ # object.expects(:method2).returns(:result2)
+ def expects(method_names, backtrace = nil)
+ method_names = method_names.is_a?(Hash) ? method_names : { method_names => nil }
+ method_names.each do |method_name, return_value|
+ expectations << Expectation.new(self, method_name, backtrace).returns(return_value)
+ self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
+ end
+ expectations.last
+ end
+
+ alias_method :__expects__, :expects
+
+ # :call-seq: stubs(method_name) -> expectation
+ # stubs(method_names) -> last expectation
+ #
+ # Adds an expectation that a method identified by +method_name+ symbol may be called any number of times with any parameters.
+ # Returns the new expectation which can be further modified by methods on Mocha::Expectation.
+ # object = mock()
+ # object.stubs(:method1)
+ # object.method1
+ # object.method1
+ # # no error raised
+ # If +method_names+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
+ # object = mock()
+ # object.stubs(:method1 => :result1, :method2 => :result2)
+ #
+ # # exactly equivalent to
+ #
+ # object = mock()
+ # object.stubs(:method1).returns(:result1)
+ # object.stubs(:method2).returns(:result2)
+ def stubs(method_names, backtrace = nil)
+ method_names = method_names.is_a?(Hash) ? method_names : { method_names => nil }
+ method_names.each do |method_name, return_value|
+ expectations << Stub.new(self, method_name, backtrace).returns(return_value)
+ self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
+ end
+ expectations.last
+ end
+
+ alias_method :__stubs__, :stubs
+
+ # :stopdoc:
+
+ def method_missing(symbol, *arguments, &block)
+ matching_expectation = matching_expectation(symbol, *arguments)
+ if matching_expectation then
+ matching_expectation.invoke(&block)
+ elsif stub_everything then
+ return
+ else
+ begin
+ super_method_missing(symbol, *arguments, &block)
+ rescue NoMethodError
+ unexpected_method_called(symbol, *arguments)
+ end
+ end
+ end
+
+ def respond_to?(symbol)
+ expectations.any? { |expectation| expectation.method_name == symbol }
+ end
+
+ def super_method_missing(symbol, *arguments, &block)
+ raise NoMethodError
+ end
+
+ def unexpected_method_called(symbol, *arguments)
+ MissingExpectation.new(self, symbol).with(*arguments).verify
+ end
+
+ def matching_expectation(symbol, *arguments)
+ expectations.reverse.detect { |expectation| expectation.match?(symbol, *arguments) }
+ end
+
+ def verify(&block)
+ expectations.each { |expectation| expectation.verify(&block) }
+ end
+
+ # :startdoc:
+
+ end
+end
View
100 trunk/lib/mocha/object.rb
@@ -0,0 +1,100 @@
+require 'mocha/mock'
+require 'mocha/instance_method'
+require 'mocha/class_method'
+require 'mocha/any_instance_method'
+
+# Methods added all Objects.
+class Object
+
+ def mocha # :nodoc:
+ @mocha ||= Mocha::Mock.new
+ end
+
+ def reset_mocha # :nodoc:
+ @mocha = nil
+ end
+
+ def stubba_method # :nodoc:
+ Mocha::InstanceMethod
+ end
+
+ def stubba_object # :nodoc:
+ self
+ end
+
+ # :call-seq: expects(symbol) -> expectation
+ #
+ # Adds an expectation that a method identified by +symbol+ must be called exactly once with any parameters.
+ # Returns the new expectation which can be further modified by methods on Mocha::Expectation.
+ # product = Product.new
+ # product.expects(:save).returns(true)
+ # assert_equal false, product.save
+ def expects(symbol)
+ method = stubba_method.new(stubba_object, symbol)
+ $stubba.stub(method)
+ mocha.expects(symbol, caller)
+ end
+
+ # :call-seq: stubs(symbol) -> expectation
+ #
+ # Adds an expectation that a method identified by +symbol+ may be called any number of times with any parameters.
+ # Returns the new expectation which can be further modified by methods on Mocha::Expectation.
+ # product = Product.new
+ # product.stubs(:save).returns(true)
+ # assert_equal false, product.save
+ def stubs(symbol)
+ method = stubba_method.new(stubba_object, symbol)
+ $stubba.stub(method)
+ mocha.stubs(symbol, caller)
+ end
+
+ def verify # :nodoc:
+ mocha.verify
+ end
+
+end
+
+class Module # :nodoc:
+
+ def stubba_method
+ Mocha::ClassMethod
+ end
+
+end
+
+class Class
+
+ def stubba_method # :nodoc:
+ Mocha::ClassMethod
+ end
+
+ class AnyInstance # :nodoc:
+
+ def initialize(klass)
+ @stubba_object = klass
+ end
+
+ def stubba_method
+ Mocha::AnyInstanceMethod
+ end
+
+ def stubba_object
+ @stubba_object
+ end
+
+ end
+
+ # :call-seq: any_instance -> mock object
+ #
+ # Returns a mock object which will detect calls to any instance of this class.
+ # Product.any_instance.stubs(:save).returns(false)
+ # product_1 = Product.new
+ # assert_equal false, product_1.save
+ # product_2 = Product.new
+ # assert_equal false, product_2.save
+ def any_instance
+ @any_instance ||= AnyInstance.new(self)
+ end
+
+end
+
View
28 trunk/lib/mocha/pretty_parameters.rb
@@ -0,0 +1,28 @@
+require 'mocha/inspect'
+
+module Mocha
+
+ class PrettyParameters
+
+ def initialize(params)
+ @params = params
+ @params_string = params.mocha_inspect
+ end
+
+ def pretty
+ remove_outer_array_braces!
+ remove_outer_hash_braces!
+ @params_string
+ end
+
+ def remove_outer_array_braces!
+ @params_string = @params_string.gsub(/^\[|\]$/, '')
+ end
+
+ def remove_outer_hash_braces!
+ @params_string = @params_string.gsub(/^\{|\}$/, '') if @params.size == 1
+ end
+
+ end
+
+end
View
23 trunk/lib/mocha/setup_and_teardown.rb
@@ -0,0 +1,23 @@
+require 'mocha/central'
+
+module Mocha
+
+ module SetupAndTeardown
+
+ def setup_stubs
+ $stubba = Mocha::Central.new
+ end
+
+ def verify_stubs
+ $stubba.verify_all { yield if block_given? } if $stubba
+ end
+
+ def teardown_stubs
+ if $stubba then
+ $stubba.unstub_all
+ $stubba = nil
+ end
+ end
+
+ end
+end
View
30 trunk/lib/mocha/standalone.rb
@@ -0,0 +1,30 @@
+require 'mocha/auto_verify'
+require 'mocha/setup_and_teardown'
+
+module Mocha
+
+ module Standalone
+
+ include AutoVerify
+ include SetupAndTeardown
+
+ def mocha_setup
+ setup_stubs
+ end
+
+ def mocha_verify(&block)
+ verify_mocks(&block)
+ verify_stubs(&block)
+ end
+
+ def mocha_teardown
+ begin
+ teardown_mocks
+ ensure
+ teardown_stubs
+ end
+ end
+
+ end
+
+end
View
49 trunk/lib/mocha/test_case_adapter.rb
@@ -0,0 +1,49 @@
+require 'mocha/expectation_error'
+
+module Mocha
+
+ module TestCaseAdapter
+
+ def self.included(base)
+ base.class_eval do
+
+ alias_method :run_before_mocha_test_case_adapter, :run
+
+ def run(result)
+ yield(Test::Unit::TestCase::STARTED, name)
+ @_result = result
+ begin
+ mocha_setup
+ begin
+ setup
+ __send__(@method_name)
+ mocha_verify { add_assertion }
+ rescue Mocha::ExpectationError => e
+ add_failure(e.message, e.backtrace)
+ rescue Test::Unit::AssertionFailedError => e
+ add_failure(e.message, e.backtrace)
+ rescue StandardError, ScriptError
+ add_error($!)
+ ensure
+ begin
+ teardown
+ rescue Test::Unit::AssertionFailedError => e
+ add_failure(e.message, e.backtrace)
+ rescue StandardError, ScriptError
+ add_error($!)
+ end
+ end
+ ensure
+ mocha_teardown
+ end
+ result.add_run
+ yield(Test::Unit::TestCase::FINISHED, name)
+ end
+
+ end
+
+ end
+
+ end
+
+end
View
2  trunk/lib/mocha_standalone.rb
@@ -0,0 +1,2 @@
+require 'mocha/standalone'
+require 'mocha/object'
View
2  trunk/lib/stubba.rb
@@ -0,0 +1,2 @@
+# for backwards compatibility
+require 'mocha'
View
36 trunk/test/active_record_test_case.rb
@@ -0,0 +1,36 @@
+module ActiveRecordTestCase
+
+ def setup_with_fixtures
+ methods_called << :setup_with_fixtures
+ end
+
+ alias_method :setup, :setup_with_fixtures
+
+ def teardown_with_fixtures
+ methods_called << :teardown_with_fixtures
+ end
+
+ alias_method :teardown, :teardown_with_fixtures
+
+ def self.method_added(method)
+ case method.to_s
+ when 'setup'
+ unless method_defined?(:setup_without_fixtures)
+ alias_method :setup_without_fixtures, :setup
+ define_method(:setup) do
+ setup_with_fixtures
+ setup_without_fixtures
+ end
+ end
+ when 'teardown'
+ unless method_defined?(:teardown_without_fixtures)
+ alias_method :teardown_without_fixtures, :teardown
+ define_method(:teardown) do
+ teardown_without_fixtures
+ teardown_with_fixtures
+ end
+ end
+ end
+ end
+
+end
View
75 trunk/test/all_tests.rb
@@ -0,0 +1,75 @@
+require 'test/unit/ui/console/testrunner'
+
+require 'mocha/inspect_test'
+require 'mocha/pretty_parameters_test'
+require 'mocha/expectation_test'
+require 'mocha/infinite_range_test'
+require 'mocha/mock_methods_test'
+require 'mocha/mock_test'
+require 'mocha/auto_verify_test'
+
+require 'mocha/central_test'
+require 'mocha/class_method_test'
+require 'mocha/any_instance_method_test'
+require 'mocha/setup_and_teardown_test'
+require 'mocha/object_test'
+require 'mocha/metaclass_test'
+
+class UnitTests
+
+ def self.suite
+ suite = Test::Unit::TestSuite.new('UnitTests')
+ suite << InspectTest.suite
+ suite << PrettyParametersTest.suite
+ suite << ExpectationTest.suite
+ suite << InfiniteRangeTest.suite
+ suite << MockMethodsTest.suite
+ suite << MockTest.suite
+ suite << AutoVerifyTest.suite
+ suite << CentralTest.suite
+ suite << ClassMethodTest.suite
+ suite << AnyInstanceMethodTest.suite
+ suite << SetupAndTeardownTest.suite
+ suite << ObjectTest.suite
+ suite << MetaclassTest.suite
+ suite
+ end
+
+end
+
+Test::Unit::UI::Console::TestRunner.run(UnitTests)
+
+require 'mocha_test_result_integration_test'
+require 'stubba_test_result_integration_test'
+require 'stubba_integration_test'
+
+class IntegrationTests
+
+ def self.suite
+ suite = Test::Unit::TestSuite.new('IntegrationTests')
+ suite << MochaTestResultIntegrationTest.suite
+ suite << StubbaTestResultIntegrationTest.suite
+ suite << StubbaIntegrationTest.suite
+ end
+
+end
+
+Test::Unit::UI::Console::TestRunner.run(IntegrationTests)
+
+require 'mocha_acceptance_test'
+require 'stubba_acceptance_test'
+require 'standalone_acceptance_test'
+
+class AcceptanceTests
+
+ def self.suite
+ suite = Test::Unit::TestSuite.new('AcceptanceTests')
+ suite << MochaAcceptanceTest.suite
+ suite << StubbaAcceptanceTest.suite
+ suite << StandaloneAcceptanceTest.suite
+ suite
+ end
+
+end
+
+Test::Unit::UI::Console::TestRunner.run(AcceptanceTests)
View
34 trunk/test/execution_point.rb
@@ -0,0 +1,34 @@
+class ExecutionPoint
+
+ attr_reader :backtrace
+
+ def self.current
+ new(caller)
+ end
+
+ def initialize(backtrace)
+ @backtrace = backtrace
+ end
+
+ def file_name
+ /\A(.*?):\d+/.match(@backtrace.first)[1]
+ end
+
+ def line_number
+ Integer(/\A.*?:(\d+)/.match(@backtrace.first)[1])
+ end
+
+ def ==(other)
+ return false unless other.is_a?(ExecutionPoint)
+ (file_name == other.file_name) and (line_number == other.line_number)
+ end
+
+ def to_s
+ "file: #{file_name} line: #{line_number}"
+ end
+
+ def inspect
+ to_s
+ end
+
+end
View
18 trunk/test/method_definer.rb
@@ -0,0 +1,18 @@
+require 'mocha/metaclass'
+
+class Object
+
+ def define_instance_method(method_symbol, &block)
+ __metaclass__.send(:define_method, method_symbol, block)
+ end
+
+ def replace_instance_method(method_symbol, &block)
+ raise "Cannot replace #{method_symbol} as #{self} does not respond to it." unless self.respond_to?(method_symbol)
+ define_instance_method(method_symbol, &block)
+ end
+
+ def define_instance_accessor(*symbols)
+ symbols.each { |symbol| __metaclass__.send(:attr_accessor, symbol) }
+ end
+
+end
View
124 trunk/test/mocha/any_instance_method_test.rb
@@ -0,0 +1,124 @@
+require File.join(File.dirname(__FILE__), "..", "test_helper")
+require 'method_definer'
+require 'mocha/mock'
+require 'mocha/any_instance_method'
+
+class AnyInstanceMethodTest < Test::Unit::TestCase
+
+ include Mocha
+
+ def test_should_hide_original_method
+ klass = Class.new { def method_x; end }
+ method = AnyInstanceMethod.new(klass, :method_x)
+ hidden_method_x = method.hidden_method.to_sym
+
+ method.hide_original_method
+
+ assert klass.method_defined?(hidden_method_x)
+ end
+
+ def test_should_not_hide_original_method_if_it_is_not_defined
+ klass = Class.new
+ method = AnyInstanceMethod.new(klass, :method_x)
+ hidden_method_x = method.hidden_method.to_sym
+
+ method.hide_original_method
+
+ assert_equal false, klass.method_defined?(hidden_method_x)
+ end
+
+ def test_should_define_a_new_method
+ klass = Class.new { def method_x; end }
+ method = AnyInstanceMethod.new(klass, :method_x)
+ mocha = Mock.new
+ mocha.expects(:method_x).with(:param1, :param2).returns(:result)
+ any_instance = Object.new
+ any_instance.define_instance_method(:mocha) { mocha }
+ klass.define_instance_method(:any_instance) { any_instance }
+
+ method.define_new_method
+
+ instance = klass.new
+ result = instance.method_x(:param1, :param2)
+
+ assert_equal :result, result
+ mocha.verify
+ end
+
+ def test_should_restore_original_method
+ klass = Class.new { def method_x; end }
+ method = AnyInstanceMethod.new(klass, :method_x)
+ hidden_method_x = method.hidden_method.to_sym
+ klass.send(:define_method, hidden_method_x, Proc.new { :original_result })
+
+ method.restore_original_method
+
+ instance = klass.new
+ assert_equal :original_result, instance.method_x
+ assert !klass.method_defined?(hidden_method_x)
+ end
+
+ def test_should_not_restore_original_method_if_hidden_method_not_defined
+ klass = Class.new { def method_x; :new_result; end }
+ method = AnyInstanceMethod.new(klass, :method_x)
+
+ method.restore_original_method
+
+ instance = klass.new
+ assert_equal :new_result, instance.method_x
+ end
+
+ def test_should_call_remove_new_method
+ klass = Class.new { def method_x; end }
+ any_instance = Mock.new
+ any_instance.stubs(:reset_mocha)
+ klass.define_instance_method(:any_instance) { any_instance }
+ method = AnyInstanceMethod.new(klass, :method_x)
+ method.replace_instance_method(:restore_original_method) { }
+ method.define_instance_accessor(:remove_called)
+ method.replace_instance_method(:remove_new_method) { self.remove_called = true }
+
+ method.unstub
+
+ assert method.remove_called
+ end
+
+ def test_should_call_restore_original_method
+ klass = Class.new { def method_x; end }
+ any_instance = Mock.new
+ any_instance.stubs(:reset_mocha)
+ klass.define_instance_method(:any_instance) { any_instance }
+ method = AnyInstanceMethod.new(klass, :method_x)
+ method.replace_instance_method(:remove_new_method) { }
+ method.define_instance_accessor(:restore_called)
+ method.replace_instance_method(:restore_original_method) { self.restore_called = true }
+
+ method.unstub
+
+ assert method.restore_called
+ end
+
+ def test_should_call_reset_mocha
+ klass = Class.new { def method_x; end }
+ any_instance = Class.new { attr_accessor :mocha_was_reset; def reset_mocha; self.mocha_was_reset = true; end }.new
+ klass.define_instance_method(:any_instance) { any_instance }
+ method = AnyInstanceMethod.new(klass, :method_x)
+ method.replace_instance_method(:remove_new_method) { }
+ method.replace_instance_method(:restore_original_method) { }
+
+ method.unstub
+
+ assert any_instance.mocha_was_reset
+ end
+
+ def test_should_return_any_instance_mocha_for_stubbee
+ mocha = Object.new
+ any_instance = Object.new
+ any_instance.define_instance_method(:mocha) { mocha }
+ stubbee = Class.new
+ stubbee.define_instance_method(:any_instance) { any_instance }
+ method = AnyInstanceMethod.new(stubbee, :method_name)
+ assert_equal stubbee.any_instance.mocha, method.mock
+ end
+
+end
View
163 trunk/test/mocha/auto_verify_test.rb
@@ -0,0 +1,163 @@
+require File.join(File.dirname(__FILE__), "..", "test_helper")
+require 'mocha/auto_verify'
+require 'method_definer'
+
+class AutoVerifyTest < Test::Unit::TestCase
+
+ attr_reader :test_case
+
+ def setup
+ @test_case = Object.new
+ class << test_case
+ def self.add_teardown_method(symbol); end
+ include Mocha::AutoVerify
+ end
+ end
+
+ def test_should_add_mock_type_expectations
+ test_case.define_instance_accessor(:expectation_type, :expectations)
+ test_case.define_instance_method(:build_mock_with_expectations) do |expectation_type, expectations|
+ self.expectation_type = expectation_type
+ self.expectations = expectations
+ end
+ expectations = { :method1 => :value1, :method2 => :value2 }
+
+ test_case.mock(expectations)
+
+ assert_equal :expects, test_case.expectation_type
+ assert_equal expectations, test_case.expectations
+ end
+
+ def test_should_add_stub_type_expectations
+ test_case.define_instance_accessor(:expectation_type, :expectations)
+ test_case.define_instance_method(:build_mock_with_expectations) do |expectation_type, expectations|
+ self.expectation_type = expectation_type
+ self.expectations = expectations
+ end
+ expectations = { :method1 => :value1, :method2 => :value2 }
+
+ test_case.stub(expectations)
+
+ assert_equal :stubs, test_case.expectation_type
+ assert_equal expectations, test_case.expectations
+ end
+
+ def test_should_add_greedy_stub_type_expectations
+ test_case.define_instance_accessor(:expectation_type, :expectations)
+ test_case.define_instance_method(:build_mock_with_expectations) do |expectation_type, expectations|
+ self.expectation_type = expectation_type
+ self.expectations = expectations
+ end
+ expectations = { :method1 => :value1, :method2 => :value2 }
+
+ test_case.stub_everything(expectations)
+
+ assert_equal :stub_everything, test_case.expectation_type
+ assert_equal expectations, test_case.expectations
+ end
+
+ def test_should_build_mock
+ mock = test_case.build_mock_with_expectations
+ assert mock.is_a?(Mocha::Mock)
+ end
+
+ def test_should_add_expectation_to_mock
+ mock = test_case.build_mock_with_expectations(:expects, :expected_method => 'return_value')
+ assert_equal 'return_value', mock.expected_method
+ end
+
+ def test_should_build_stub
+ stub = test_case.build_mock_with_expectations(:stubs)
+ assert stub.is_a?(Mocha::Mock)
+ end
+
+ def test_should_add_expectation_to_stub
+ stub = test_case.build_mock_with_expectations(:stubs, :stubbed_method => 'return_value')
+ assert_equal 'return_value', stub.stubbed_method
+ end
+
+ def test_should_build_greedy_stub
+ greedy_stub = test_case.build_mock_with_expectations(:stub_everything)
+ assert greedy_stub.stub_everything
+ end
+
+ def test_should_add_expectations_to_greedy_stub
+ greedy_mock = test_case.build_mock_with_expectations(:stub_everything, :stubbed_method => 'return_value')
+ assert_equal 'return_value', greedy_mock.stubbed_method
+ end
+
+ def test_should_build_new_mock_each_time
+ assert_not_equal test_case.build_mock_with_expectations, test_case.build_mock_with_expectations
+ end
+
+ def test_should_store_each_new_mock
+ expected = Array.new(3) { test_case.build_mock_with_expectations }
+ assert_equal expected, test_case.mocks
+ end
+
+ def test_should_verify_each_mock
+ mocks = Array.new(3) do
+ mock = Object.new
+ mock.define_instance_accessor(:verify_called)
+ mock.define_instance_method(:verify) { self.verify_called = true }
+ mock
+ end
+ test_case.replace_instance_method(:mocks) { mocks }
+ test_case.verify_mocks
+ assert mocks.all? { |mock| mock.verify_called }
+ end
+
+ def test_should_yield_to_block_for_each_assertion
+ mock_class = Class.new do
+ def verify(&block); yield; end
+ end
+ mock = mock_class.new
+ test_case.replace_instance_method(:mocks) { [mock] }
+ yielded = false
+ test_case.verify_mocks { yielded = true }
+ assert yielded
+ end
+
+ def test_should_reset_mocks_on_teardown
+ mock = Class.new { define_method(:verify) {} }.new
+ test_case.mocks << mock
+ test_case.teardown_mocks
+ assert test_case.mocks.empty?
+ end
+
+ def test_should_stub_everything
+ mock = test_case.stub_everything
+ assert_equal true, mock.stub_everything
+ end
+
+ def test_should_add_mock_to_mocks
+ mock = test_case.mock
+ assert_equal [mock], test_case.mocks
+ end
+
+ def test_should_add_stub_to_mocks
+ stub = test_case.stub
+ assert_equal [stub], test_case.mocks
+ end
+
+ def test_should_add_greedy_stub_to_mocks
+ greedy_stub = test_case.stub_everything
+ assert_equal [greedy_stub], test_case.mocks
+ end
+
+ def test_should_create_mock_with_name
+ mock = test_case.mock('named_mock')
+ assert_equal '#<Mock:named_mock>', mock.mocha_inspect
+ end
+
+ def test_should_create_stub_with_name
+ stub = test_case.stub('named_stub')
+ assert_equal '#<Mock:named_stub>', stub.mocha_inspect
+ end
+
+ def test_should_create_greedy_stub_with_name
+ greedy_stub = test_case.stub_everything('named_greedy_stub')
+ assert_equal '#<Mock:named_greedy_stub>', greedy_stub.mocha_inspect
+ end
+
+end
View
124 trunk/test/mocha/central_test.rb
@@ -0,0 +1,124 @@
+require File.join(File.dirname(__FILE__), "..", "test_helper")
+
+require 'mocha/central'
+require 'mocha/mock'
+require 'method_definer'
+
+class CentralTest < Test::Unit::TestCase
+
+ include Mocha
+
+ def test_should_start_with_empty_stubba_methods
+ stubba = Central.new
+
+ assert_equal [], stubba.stubba_methods
+ end
+
+ def test_should_stub_method_if_not_already_stubbed
+ method = Mock.new
+ method.expects(:stub)
+ stubba = Central.new
+
+ stubba.stub(method)
+
+ method.verify
+ end
+
+ def test_should_not_stub_method_if_already_stubbed
+ method = Mock.new
+ method.expects(:stub).times(0)
+ stubba = Central.new
+ stubba_methods = Mock.new
+ stubba_methods.stubs(:include?).with(method).returns(true)
+ stubba.stubba_methods = stubba_methods
+
+ stubba.stub(method)
+
+ method.verify
+ end
+
+ def test_should_record_method
+ method = Mock.new
+ method.expects(:stub)
+ stubba = Central.new
+
+ stubba.stub(method)
+
+ assert_equal [method], stubba.stubba_methods
+ end
+
+ def test_should_unstub_all_methods
+ stubba = Central.new
+ method_1 = Mock.new
+ method_1.expects(:unstub)
+ method_2 = Mock.new
+ method_2.expects(:unstub)
+ stubba.stubba_methods = [method_1, method_2]
+
+ stubba.unstub_all
+
+ assert_equal [], stubba.stubba_methods
+ method_1.verify
+ method_2.verify
+ end
+
+ def test_should_collect_mocks_from_all_methods
+ method_1 = Mock.new
+ method_1.stubs(:mock).returns(:mock_1)
+
+ method_2 = Mock.new
+ method_2.stubs(:mock).returns(:mock_2)
+
+ stubba = Central.new
+ stubba.stubba_methods = [method_1, method_2]
+
+ assert_equal 2, stubba.unique_mocks.size
+ assert stubba.unique_mocks.include?(:mock_1)
+ assert stubba.unique_mocks.include?(:mock_2)
+ end
+
+ def test_should_return_unique_mochas
+ method_1 = Mock.new
+ method_1.stubs(:mock).returns(:mock_1)
+
+ method_2 = Mock.new
+ method_2.stubs(:mock).returns(:mock_1)
+
+ stubba = Central.new
+ stubba.stubba_methods = [method_1, method_2]
+
+ assert_equal [:mock_1], stubba.unique_mocks
+ end
+
+ def test_should_call_verify_on_all_unique_mocks
+ mock_class = Class.new do
+ attr_accessor :verify_called
+ def verify
+ self.verify_called = true
+ end
+ end
+ mocks = [mock_class.new, mock_class.new]
+ stubba = Central.new
+ stubba.replace_instance_method(:unique_mocks) { mocks }
+
+ stubba.verify_all
+
+ assert mocks.all? { |mock| mock.verify_called }
+ end
+
+ def test_should_call_verify_on_all_unique_mochas
+ mock_class = Class.new do
+ def verify(&block)
+ yield if block_given?
+ end
+ end
+ stubba = Central.new
+ stubba.replace_instance_method(:unique_mocks) { [mock_class.new] }
+ yielded = false
+
+ stubba.verify_all { yielded = true }
+
+ assert yielded
+ end
+
+end
View
196 trunk/test/mocha/class_method_test.rb
@@ -0,0 +1,196 @@
+require File.join(File.dirname(__FILE__), "..", "test_helper")
+require 'method_definer'
+require 'mocha/mock'
+
+require 'mocha/class_method'
+
+class ClassMethodTest < Test::Unit::TestCase
+
+ include Mocha
+
+ def test_should_provide_hidden_version_of_method_name_starting_with_prefix
+ method = ClassMethod.new(nil, :original_method_name)
+ assert_match /^__stubba__/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_ending_with_suffix
+ method = ClassMethod.new(nil, :original_method_name)
+ assert_match /__stubba__$/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_including_original_method_name
+ method = ClassMethod.new(nil, :original_method_name)
+ assert_match /original_method_name/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_substituting_question_mark
+ method = ClassMethod.new(nil, :question_mark?)
+ assert_no_match /\?/, method.hidden_method
+ assert_match /question_mark_substituted_character_63/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_substituting_exclamation_mark
+ method = ClassMethod.new(nil, :exclamation_mark!)
+ assert_no_match /!/, method.hidden_method
+ assert_match /exclamation_mark_substituted_character_33/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_substituting_equals_sign
+ method = ClassMethod.new(nil, :equals_sign=)
+ assert_no_match /\=/, method.hidden_method
+ assert_match /equals_sign_substituted_character_61/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_substituting_brackets
+ method = ClassMethod.new(nil, :[])
+ assert_no_match /\[\]/, method.hidden_method
+ assert_match /substituted_character_91__substituted_character_93/, method.hidden_method
+ end
+
+ def test_should_provide_hidden_version_of_method_name_substituting_plus_sign
+ method = ClassMethod.new(nil, :+)
+ assert_no_match /\+/, method.hidden_method
+ assert_match /substituted_character_43/, method.hidden_method
+ end
+
+ def test_should_hide_original_method
+ klass = Class.new { def self.method_x; end }
+ method = ClassMethod.new(klass, :method_x)
+ hidden_method_x = method.hidden_method
+
+ method.hide_original_method
+
+ assert klass.respond_to?(hidden_method_x)
+ end
+
+ def test_should_respond_to_original_method_name_after_original_method_has_been_hidden
+ klass = Class.new { def self.original_method_name; end }
+ method = ClassMethod.new(klass, :original_method_name)
+ hidden_method_x = method.hidden_method
+
+ method.hide_original_method
+
+ assert klass.respond_to?(:original_method_name)
+ end
+
+ def test_should_not_hide_original_method_if_method_not_defined
+ klass = Class.new
+ method = ClassMethod.new(klass, :method_x)
+ hidden_method_x = method.hidden_method
+
+ method.hide_original_method
+
+ assert_equal false, klass.respond_to?(hidden_method_x)
+ end
+
+ def test_should_define_a_new_method_which_should_call_mocha_method_missing
+ klass = Class.new { def self.method_x; end }
+ mocha = Mocha::Mock.new
+ klass.define_instance_method(:mocha) { mocha }
+ mocha.expects(:method_x).with(:param1, :param2).returns(:result)
+ method = ClassMethod.new(klass, :method_x)
+
+ method.define_new_method
+ result = klass.method_x(:param1, :param2)
+
+ assert_equal :result, result
+ mocha.verify
+ end
+
+ def test_should_remove_new_method
+ klass = Class.new { def self.method_x; end }
+ method = ClassMethod.new(klass, :method_x)
+
+ method.remove_new_method
+
+ assert_equal false, klass.respond_to?(:method_x)
+ end
+
+ def test_should_restore_original_method
+ klass = Class.new { def self.method_x; end }
+ method = ClassMethod.new(klass, :method_x)
+ hidden_method_x = method.hidden_method.to_sym
+ klass.define_instance_method(hidden_method_x) { :original_result }
+
+ method.restore_original_method
+
+ assert_equal :original_result, klass.method_x
+ assert_equal false, klass.respond_to?(hidden_method_x)
+ end
+
+ def test_should_not_restore_original_method_if_hidden_method_is_not_defined
+ klass = Class.new { def self.method_x; :new_result; end }
+ method = ClassMethod.new(klass, :method_x)
+
+ method.restore_original_method
+
+ assert_equal :new_result, klass.method_x
+ end
+
+ def test_should_call_hide_original_method
+ klass = Class.new { def self.method_x; end }
+ method = ClassMethod.new(klass, :method_x)
+ method.define_instance_accessor(:hide_called)
+ method.replace_instance_method(:hide_original_method) { self.hide_called = true }
+
+ method.stub
+
+ assert method.hide_called
+ end
+
+ def test_should_call_define_new_method
+ klass = Class.new { def self.method_x; end }
+ method = ClassMethod.new(klass, :method_x)
+ method.define_instance_accessor(:define_called)
+ method.replace_instance_method(:define_new_method) { self.define_called = true }
+
+ method.stub
+
+ assert method.define_called
+ end
+
+ def test_should_call_remove_new_method
+ klass = Class.new { def self.method_x; end }
+ klass.define_instance_method(:reset_mocha) { }
+ method = ClassMethod.new(klass, :method_x)
+ method.define_instance_accessor(:remove_called)
+ method.replace_instance_method(:remove_new_method) { self.remove_called = true }
+
+ method.unstub
+
+ assert method.remove_called
+ end
+
+ def test_should_call_restore_original_method
+ klass = Class.new { def self.method_x; end }
+ klass.define_instance_method(:reset_mocha) { }
+ method = ClassMethod.new(klass, :method_x)
+ method.define_instance_accessor(:restore_called)
+ method.replace_instance_method(:restore_original_method) { self.restore_called = true }
+
+ method.unstub
+
+ assert method.restore_called
+ end
+
+ def test_should_call_reset_mocha
+ klass = Class.new { def self.method_x; end }
+ klass.define_instance_accessor(:reset_called)
+ klass.define_instance_method(:reset_mocha) { self.reset_called = true }
+ method = ClassMethod.new(klass, :method_x)
+ method.replace_instance_method(:restore_original_method) { }
+
+ method.unstub
+
+ assert klass.reset_called
+ end
+
+ def test_should_return_mock_for_stubbee
+ mocha = Object.new
+ stubbee = Object.new
+ stubbee.define_instance_accessor(:mocha) { mocha }
+ method = ClassMethod.new(stubbee, :method_name)
+ assert_equal stubbee.mocha, method.mock
+ end
+
+end
View
357 trunk/test/mocha/expectation_test.rb
@@ -0,0 +1,357 @@
+require File.join(File.dirname(__FILE__), "..", "test_helper")
+require 'method_definer'
+require 'mocha/expectation'
+require 'execution_point'
+
+class ExpectationTest < Test::Unit::TestCase
+
+ include Mocha
+
+ def new_expectation
+ Expectation.new(nil, :expected_method)
+ end
+
+ def test_should_match_calls_to_same_method_with_any_parameters
+ assert new_expectation.match?(:expected_method, 1, 2, 3)
+ end
+
+ def test_should_match_calls_to_same_method_with_exactly_zero_parameters
+ expectation = new_expectation.with()
+ assert expectation.match?(:expected_method)
+ end
+
+ def test_should_not_match_calls_to_same_method_with_more_than_zero_parameters
+ expectation = new_expectation.with()
+ assert !expectation.match?(:expected_method, 1, 2, 3)
+ end
+
+ def test_should_match_calls_to_same_method_with_expected_parameter_values
+ expectation = new_expectation.with(1, 2, 3)
+ assert expectation.match?(:expected_method, 1, 2, 3)
+ end
+
+ def test_should_match_calls_to_same_method_with_parameters_constrained_as_expected