mocha 0.5.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/README +4 -4
  2. data/RELEASE +45 -0
  3. data/Rakefile +55 -33
  4. data/lib/mocha.rb +1 -0
  5. data/lib/mocha/any_instance_method.rb +24 -4
  6. data/lib/mocha/backtrace_filter.rb +17 -0
  7. data/lib/mocha/cardinality.rb +92 -0
  8. data/lib/mocha/central.rb +1 -9
  9. data/lib/mocha/change_state_side_effect.rb +19 -0
  10. data/lib/mocha/class_method.rb +25 -5
  11. data/lib/mocha/configuration.rb +60 -0
  12. data/lib/mocha/exception_raiser.rb +1 -1
  13. data/lib/mocha/expectation.rb +109 -48
  14. data/lib/mocha/expectation_error.rb +6 -6
  15. data/lib/mocha/expectation_list.rb +10 -14
  16. data/lib/mocha/in_state_ordering_constraint.rb +19 -0
  17. data/lib/mocha/instance_method.rb +9 -0
  18. data/lib/mocha/logger.rb +15 -0
  19. data/lib/mocha/mock.rb +19 -14
  20. data/lib/mocha/mockery.rb +166 -0
  21. data/lib/mocha/module_method.rb +17 -0
  22. data/lib/mocha/names.rb +53 -0
  23. data/lib/mocha/object.rb +26 -9
  24. data/lib/mocha/parameter_matchers.rb +2 -1
  25. data/lib/mocha/parameter_matchers/all_of.rb +3 -3
  26. data/lib/mocha/parameter_matchers/any_of.rb +3 -3
  27. data/lib/mocha/parameter_matchers/anything.rb +1 -1
  28. data/lib/mocha/parameter_matchers/has_entries.rb +4 -1
  29. data/lib/mocha/parameter_matchers/has_entry.rb +3 -2
  30. data/lib/mocha/parameter_matchers/has_key.rb +1 -1
  31. data/lib/mocha/parameter_matchers/has_value.rb +1 -1
  32. data/lib/mocha/parameter_matchers/not.rb +2 -2
  33. data/lib/mocha/parameter_matchers/object.rb +1 -1
  34. data/lib/mocha/parameter_matchers/optionally.rb +22 -0
  35. data/lib/mocha/parameter_matchers/regexp_matches.rb +2 -2
  36. data/lib/mocha/parameter_matchers/responds_with.rb +43 -0
  37. data/lib/mocha/parameter_matchers/yaml_equivalent.rb +43 -0
  38. data/lib/mocha/single_return_value.rb +2 -9
  39. data/lib/mocha/standalone.rb +151 -17
  40. data/lib/mocha/state_machine.rb +91 -0
  41. data/lib/mocha/stubbing_error.rb +16 -0
  42. data/lib/mocha/test_case_adapter.rb +76 -22
  43. data/lib/stubba.rb +2 -1
  44. data/test/acceptance/acceptance_test_helper.rb +38 -0
  45. data/test/acceptance/bug_18914_test.rb +43 -0
  46. data/test/acceptance/{expected_invocation_count_acceptance_test.rb → expected_invocation_count_test.rb} +29 -20
  47. data/test/acceptance/failure_messages_test.rb +64 -0
  48. data/test/acceptance/{mocha_acceptance_test.rb → mocha_example_test.rb} +5 -5
  49. data/test/{integration/mocha_test_result_integration_test.rb → acceptance/mocha_test_result_test.rb} +19 -40
  50. data/test/acceptance/mock_test.rb +100 -0
  51. data/test/acceptance/{mock_with_initializer_block_acceptance_test.rb → mock_with_initializer_block_test.rb} +12 -5
  52. data/test/acceptance/{mocked_methods_dispatch_acceptance_test.rb → mocked_methods_dispatch_test.rb} +12 -5
  53. data/test/acceptance/{optional_parameters_acceptance_test.rb → optional_parameters_test.rb} +11 -4
  54. data/test/acceptance/{parameter_matcher_acceptance_test.rb → parameter_matcher_test.rb} +67 -5
  55. data/test/acceptance/{partial_mocks_acceptance_test.rb → partial_mocks_test.rb} +12 -5
  56. data/test/acceptance/return_value_test.rb +52 -0
  57. data/test/acceptance/{sequence_acceptance_test.rb → sequence_test.rb} +13 -6
  58. data/test/acceptance/{standalone_acceptance_test.rb → standalone_test.rb} +19 -11
  59. data/test/acceptance/states_test.rb +70 -0
  60. data/test/acceptance/stub_any_instance_method_test.rb +195 -0
  61. data/test/acceptance/stub_class_method_test.rb +203 -0
  62. data/test/acceptance/stub_everything_test.rb +56 -0
  63. data/test/acceptance/stub_instance_method_test.rb +165 -0
  64. data/test/acceptance/stub_module_method_test.rb +163 -0
  65. data/test/acceptance/stub_test.rb +52 -0
  66. data/test/acceptance/{stubba_acceptance_test.rb → stubba_example_test.rb} +1 -1
  67. data/test/{integration/stubba_test_result_integration_test.rb → acceptance/stubba_test_result_test.rb} +17 -36
  68. data/test/acceptance/stubbing_error_backtrace_test.rb +64 -0
  69. data/test/acceptance/stubbing_method_unnecessarily_test.rb +65 -0
  70. data/test/acceptance/stubbing_non_existent_any_instance_method_test.rb +130 -0
  71. data/test/acceptance/stubbing_non_existent_class_method_test.rb +155 -0
  72. data/test/acceptance/stubbing_non_existent_instance_method_test.rb +145 -0
  73. data/test/acceptance/stubbing_non_public_any_instance_method_test.rb +130 -0
  74. data/test/acceptance/stubbing_non_public_class_method_test.rb +161 -0
  75. data/test/acceptance/stubbing_non_public_instance_method_test.rb +141 -0
  76. data/test/acceptance/stubbing_on_non_mock_object_test.rb +64 -0
  77. data/test/execution_point.rb +3 -1
  78. data/test/simple_counter.rb +13 -0
  79. data/test/test_helper.rb +0 -1
  80. data/test/test_runner.rb +6 -4
  81. data/test/unit/any_instance_method_test.rb +1 -1
  82. data/test/unit/array_inspect_test.rb +1 -1
  83. data/test/unit/backtrace_filter_test.rb +19 -0
  84. data/test/unit/cardinality_test.rb +56 -0
  85. data/test/unit/central_test.rb +4 -63
  86. data/test/unit/change_state_side_effect_test.rb +41 -0
  87. data/test/unit/class_method_test.rb +38 -1
  88. data/test/unit/date_time_inspect_test.rb +1 -1
  89. data/test/unit/{expectation_raiser_test.rb → exception_raiser_test.rb} +14 -0
  90. data/test/unit/expectation_list_test.rb +4 -22
  91. data/test/unit/expectation_test.rb +70 -94
  92. data/test/unit/in_state_ordering_constraint_test.rb +43 -0
  93. data/test/unit/mock_test.rb +16 -37
  94. data/test/unit/mockery_test.rb +149 -0
  95. data/test/unit/{no_yield_test.rb → no_yields_test.rb} +0 -0
  96. data/test/unit/object_test.rb +6 -89
  97. data/test/unit/parameter_matchers/equals_test.rb +25 -0
  98. data/test/unit/parameter_matchers/has_entries_test.rb +22 -1
  99. data/test/unit/parameter_matchers/has_entry_test.rb +24 -2
  100. data/test/unit/parameter_matchers/has_key_test.rb +11 -0
  101. data/test/unit/parameter_matchers/has_value_test.rb +12 -0
  102. data/test/unit/parameter_matchers/regexp_matches_test.rb +1 -1
  103. data/test/unit/parameter_matchers/responds_with_test.rb +25 -0
  104. data/test/unit/parameter_matchers/stub_matcher.rb +4 -0
  105. data/test/unit/parameter_matchers/yaml_equivalent_test.rb +25 -0
  106. data/test/unit/single_return_value_test.rb +0 -19
  107. data/test/unit/state_machine_test.rb +98 -0
  108. metadata +108 -69
  109. data/lib/mocha/auto_verify.rb +0 -118
  110. data/lib/mocha/infinite_range.rb +0 -25
  111. data/lib/mocha/missing_expectation.rb +0 -17
  112. data/lib/mocha/setup_and_teardown.rb +0 -23
  113. data/lib/mocha/stub.rb +0 -18
  114. data/test/integration/stubba_integration_test.rb +0 -89
  115. data/test/unit/auto_verify_test.rb +0 -129
  116. data/test/unit/expectation_error_test.rb +0 -24
  117. data/test/unit/infinite_range_test.rb +0 -53
  118. data/test/unit/missing_expectation_test.rb +0 -42
  119. data/test/unit/setup_and_teardown_test.rb +0 -76
  120. data/test/unit/stub_test.rb +0 -24
data/README CHANGED
@@ -1,12 +1,12 @@
1
1
  = Mocha
2
2
 
3
- 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.
3
+ Mocha is a library for mocking and stubbing using a syntax like that of JMock[http://www.jmock.org].
4
4
 
5
- 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.
5
+ It can be used with many testing frameworks e.g. Test::Unit[http://www.ruby-doc.org/core/classes/Test/Unit.html], RSpec[http://rspec.info/], test/spec[http://chneukirchen.org/repos/testspec/README], expectations[http://expectations.rubyforge.org/], Dust[http://dust.rubyforge.org/] and even JtestR[http://jtestr.codehaus.org/].
6
6
 
7
- Mocha provides a unified, simple and readable syntax for both traditional mocking and for mocking with _real_ objects.
7
+ Mocha provides a unified, simple and readable syntax for both traditional mocking and partial mocking.
8
8
 
9
- 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.
9
+ Mocha was harvested from projects at Reevoo[http://www.reevoo.com] by me (James[http://blog.floehopper.org]) and my colleagues Ben[http://www.techbelly.com/], Chris[http://blog.seagul.co.uk] and Paul[http://po-ru.com].
10
10
 
11
11
  == Download and Installation
12
12
 
data/RELEASE CHANGED
@@ -1,3 +1,48 @@
1
+ = 0.9.0
2
+
3
+ * Configurable warnings or errors
4
+ * when a method on a non-public method is stubbed
5
+ * when a method on a non-existent method is stubbed
6
+ * when a method on a non-mock object is stubbed
7
+ * when a method is stubbed unnecessarily (i.e. the stubbed method is not called during the test)
8
+
9
+ * Improved error messages
10
+ * User-friendly list of unsatisfied expectations, satisfied expectations and state machines.
11
+ * Improved readability of cardinality description.
12
+ * Display sensible failure message for any_instance expectations e.g. "#<AnyInstance:Foo>.bar - expected calls: 1, actual calls: 0"
13
+
14
+ * Parameter matchers
15
+ * New to this release
16
+ * optionally (allows matching of optional parameters if available)
17
+ * yaml_equivalent (allows matching of YAML that represents the specified object)
18
+ * responds_with (tests the quack not the duck)
19
+ * Nesting of parameter matchers is now supported.
20
+
21
+ * Optional block passed into mock initializer is evaluated in the context of the new mock instance and can be used as a shortcut to set up expectations.
22
+
23
+ * Added JMock-style sequences for constraining the order of expected invocations. See Standalone#sequence and Expectation#in_sequence.
24
+
25
+ * Added JMock-style states for constraining the order of expected invocations. See Standalone#states, Expectation#then, Expectation#when and StateMachine.
26
+
27
+ * Compatibility with versions of Ruby
28
+ * Compatibility with Ruby v1.9. All test errors and warnings fixed.
29
+ * Nasty fix so that TestCaseAdaptor works consistently with earlier versions of Test::Unit as well as more recent versions.
30
+ * Added platform to gem specification to avoid bug in rubygems 0.9.5 - see http://www.dcmanges.com/blog/rubygems-0-9-5-platform-bug and http://rubygems.org/read/chapter/20#platform.
31
+ * Make ExpectationRaiser deal with subclasses of Interrupt which seem to need a message supplied in the raise statement in Ruby 1.8.6 (but not 1.8.4 or 1.9). Not sure this is really Mocha's responsibility.
32
+
33
+ * Added deprecation warning in stubba.rb which is no longer needed and will be removed.
34
+
35
+ * Supply positioning information to evals to improve any error messages. See http://ola-bini.blogspot.com/2008/01/ruby-antipattern-using-eval-without.html
36
+
37
+ * Bug fixes
38
+ * 18914 in revision 296 - http://rubyforge.org/tracker/index.php?func=detail&aid=18914&group_id=1917&atid=7477
39
+ * 18917 in revision 295 - http://rubyforge.org/tracker/index.php?func=detail&aid=18917&group_id=1917&atid=7477
40
+ * 18336 in revision 287 - http://rubyforge.org/tracker/index.php?func=detail&aid=18336&group_id=1917&atid=7477
41
+ * 17835 in revision 255 - http://rubyforge.org/tracker/index.php?func=detail&aid=17835&group_id=1917&atid=7477
42
+ * 17412 in revision 242 - http://rubyforge.org/tracker/index.php?func=detail&aid=17412&group_id=1917&atid=7477
43
+ * 15977 in revision 198 - http://rubyforge.org/tracker/index.php?func=detail&aid=15977&group_id=1917&atid=7477
44
+ * 11885 in revision 156 - http://rubyforge.org/tracker/index.php?func=detail&aid=11885&group_id=1917&atid=7477
45
+
1
46
  = 0.5.5 (r167)
2
47
 
3
48
  - Renamed Matches parameter matcher to RegexpMatches for clarity.
data/Rakefile CHANGED
@@ -5,50 +5,72 @@ require 'rake/testtask'
5
5
  require 'rake/contrib/sshpublisher'
6
6
 
7
7
  module Mocha
8
- VERSION = "0.5.6"
8
+ VERSION = "0.9.0"
9
9
  end
10
10
 
11
11
  desc "Run all tests"
12
- task :default => :test_all
13
-
14
- task :test_all => [:test_unit, :test_integration, :test_acceptance]
15
-
16
- desc "Run unit tests"
17
- Rake::TestTask.new(:test_unit) do |t|
18
- t.libs << 'test'
19
- t.test_files = FileList['test/unit/**/*_test.rb']
20
- t.verbose = true
21
- t.warning = true
22
- end
12
+ task 'default' => ['test:units', 'test:acceptance']
13
+
14
+ namespace 'test' do
15
+
16
+ unit_tests = FileList['test/unit/**/*_test.rb']
17
+ acceptance_tests = FileList['test/acceptance/*_test.rb']
18
+
19
+ desc "Run unit tests"
20
+ Rake::TestTask.new('units') do |t|
21
+ t.libs << 'test'
22
+ t.test_files = unit_tests
23
+ t.verbose = true
24
+ t.warning = true
25
+ end
23
26
 
24
- desc "Run integration tests"
25
- Rake::TestTask.new(:test_integration) do |t|
26
- t.libs << 'test'
27
- t.test_files = FileList['test/integration/*_test.rb']
28
- t.verbose = true
29
- t.warning = true
30
- end
27
+ desc "Run acceptance tests"
28
+ Rake::TestTask.new('acceptance') do |t|
29
+ t.libs << 'test'
30
+ t.test_files = acceptance_tests
31
+ t.verbose = true
32
+ t.warning = true
33
+ end
34
+
35
+ # require 'rcov/rcovtask'
36
+ # Rcov::RcovTask.new('coverage') do |t|
37
+ # t.libs << 'test'
38
+ # t.test_files = unit_tests + acceptance_tests
39
+ # t.verbose = true
40
+ # t.warning = true
41
+ # t.rcov_opts << '--sort coverage'
42
+ # t.rcov_opts << '--xref'
43
+ # end
31
44
 
32
- desc "Run acceptance tests"
33
- Rake::TestTask.new(:test_acceptance) do |t|
34
- t.libs << 'test'
35
- t.test_files = FileList['test/acceptance/*_test.rb']
36
- t.verbose = true
37
- t.warning = true
38
45
  end
39
46
 
40
47
  desc 'Generate RDoc'
41
- Rake::RDocTask.new do |task|
48
+ Rake::RDocTask.new('rdoc') do |task|
42
49
  task.main = 'README'
43
50
  task.title = "Mocha #{Mocha::VERSION}"
44
51
  task.rdoc_dir = 'doc'
45
52
  task.template = File.expand_path(File.join(File.dirname(__FILE__), "templates", "html_with_google_analytics"))
46
- task.rdoc_files.include('README', 'RELEASE', 'COPYING', 'MIT-LICENSE', 'agiledox.txt', 'lib/mocha/auto_verify.rb', 'lib/mocha/mock.rb', 'lib/mocha/expectation.rb', 'lib/mocha/object.rb', 'lib/mocha/parameter_matchers.rb', 'lib/mocha/parameter_matchers')
53
+ task.rdoc_files.include(
54
+ 'README',
55
+ 'RELEASE',
56
+ 'COPYING',
57
+ 'MIT-LICENSE',
58
+ 'agiledox.txt',
59
+ 'lib/mocha/standalone.rb',
60
+ 'lib/mocha/mock.rb',
61
+ 'lib/mocha/expectation.rb',
62
+ 'lib/mocha/object.rb',
63
+ 'lib/mocha/parameter_matchers.rb',
64
+ 'lib/mocha/parameter_matchers',
65
+ 'lib/mocha/state_machine.rb',
66
+ 'lib/mocha/configuration.rb',
67
+ 'lib/mocha/stubbing_error.rb'
68
+ )
47
69
  end
48
- task :rdoc => :examples
70
+ task 'rdoc' => 'examples'
49
71
 
50
72
  desc "Upload RDoc to RubyForge"
51
- task :publish_rdoc => [:rdoc, :examples] do
73
+ task 'publish_rdoc' => ['rdoc', 'examples'] do
52
74
  Rake::SshDirPublisher.new("jamesmead@rubyforge.org", "/var/www/gforge-projects/mocha", "doc").upload
53
75
  end
54
76
 
@@ -69,7 +91,7 @@ file 'agiledox.txt' do
69
91
  end
70
92
 
71
93
  desc "Convert example ruby files to syntax-highlighted html"
72
- task :examples do
94
+ task 'examples' do
73
95
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "vendor", "coderay-0.7.4.215", "lib"))
74
96
  require 'coderay'
75
97
  mkdir_p 'doc/examples'
@@ -119,16 +141,16 @@ Rake::GemPackageTask.new(specification) do |package|
119
141
  package.need_tar = true
120
142
  end
121
143
 
122
- task :verify_user do
144
+ task 'verify_user' do
123
145
  raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
124
146
  end
125
147
 
126
- task :verify_password do
148
+ task 'verify_password' do
127
149
  raise "RUBYFORGE_PASSWORD environment variable not set!" unless ENV['RUBYFORGE_PASSWORD']
128
150
  end
129
151
 
130
152
  desc "Publish package files on RubyForge."
131
- task :publish_packages => [:verify_user, :verify_password, :package] do
153
+ task 'publish_packages' => ['verify_user', 'verify_password', 'package'] do
132
154
  $:.unshift File.expand_path(File.join(File.dirname(__FILE__), "vendor", "meta_project-0.4.15", "lib"))
133
155
  require 'meta_project'
134
156
  require 'rake/contrib/xforge'
@@ -1,5 +1,6 @@
1
1
  require 'mocha_standalone'
2
2
  require 'mocha/test_case_adapter'
3
+ require 'mocha/configuration'
3
4
 
4
5
  require 'test/unit/testcase'
5
6
 
@@ -15,21 +15,41 @@ module Mocha
15
15
  end
16
16
 
17
17
  def hide_original_method
18
- stubbee.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.method_defined?(method)
18
+ if method_exists?(method)
19
+ begin
20
+ stubbee.class_eval("alias_method :#{hidden_method}, :#{method}", __FILE__, __LINE__)
21
+ rescue NameError
22
+ # deal with nasties like ActiveRecord::Associations::AssociationProxy
23
+ end
24
+ end
19
25
  end
20
26
 
21
27
  def define_new_method
22
- stubbee.class_eval "def #{method}(*args, &block); self.class.any_instance.mocha.method_missing(:#{method}, *args, &block); end"
28
+ stubbee.class_eval("def #{method}(*args, &block); self.class.any_instance.mocha.method_missing(:#{method}, *args, &block); end", __FILE__, __LINE__)
23
29
  end
24
30
 
25
31
  def remove_new_method
26
- stubbee.class_eval "remove_method :#{method}"
32
+ stubbee.class_eval("remove_method :#{method}", __FILE__, __LINE__)
27
33
  end
28
34
 
29
35
  def restore_original_method
30
- stubbee.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.method_defined?(hidden_method)
36
+ if method_exists?(hidden_method)
37
+ begin
38
+ stubbee.class_eval("alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}", __FILE__, __LINE__)
39
+ rescue NameError
40
+ # deal with nasties like ActiveRecord::Associations::AssociationProxy
41
+ end
42
+ end
31
43
  end
32
44
 
45
+ def method_exists?(method)
46
+ existing_methods = []
47
+ existing_methods += stubbee.public_instance_methods(false)
48
+ existing_methods += stubbee.protected_instance_methods(false)
49
+ existing_methods += stubbee.private_instance_methods(false)
50
+ existing_methods.any? { |m| m.to_s == method.to_s }
51
+ end
52
+
33
53
  end
34
54
 
35
55
  end
@@ -0,0 +1,17 @@
1
+ module Mocha
2
+
3
+ class BacktraceFilter
4
+
5
+ LIB_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR
6
+
7
+ def initialize(lib_directory = LIB_DIRECTORY)
8
+ @lib_directory = lib_directory
9
+ end
10
+
11
+ def filtered(backtrace)
12
+ backtrace.reject { |location| Regexp.new(@lib_directory).match(File.expand_path(location)) }
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,92 @@
1
+ module Mocha
2
+
3
+ class Cardinality
4
+
5
+ INFINITY = 1 / 0.0
6
+
7
+ class << self
8
+
9
+ def exactly(count)
10
+ new(count, count)
11
+ end
12
+
13
+ def at_least(count)
14
+ new(count, INFINITY)
15
+ end
16
+
17
+ def at_most(count)
18
+ new(0, count)
19
+ end
20
+
21
+ def times(range_or_count)
22
+ case range_or_count
23
+ when Range
24
+ new(range_or_count.first, range_or_count.last)
25
+ else
26
+ new(range_or_count, range_or_count)
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ def initialize(required, maximum)
33
+ @required, @maximum = required, maximum
34
+ end
35
+
36
+ def invocations_allowed?(invocation_count)
37
+ invocation_count < maximum
38
+ end
39
+
40
+ def satisfied?(invocations_so_far)
41
+ invocations_so_far >= required
42
+ end
43
+
44
+ def needs_verifying?
45
+ !allowed_any_number_of_times?
46
+ end
47
+
48
+ def verified?(invocation_count)
49
+ (invocation_count >= required) && (invocation_count <= maximum)
50
+ end
51
+
52
+ def allowed_any_number_of_times?
53
+ required == 0 && infinite?(maximum)
54
+ end
55
+
56
+ def used?(invocation_count)
57
+ (invocation_count > 0) || (maximum == 0)
58
+ end
59
+
60
+ def mocha_inspect
61
+ if allowed_any_number_of_times?
62
+ "allowed any number of times"
63
+ else
64
+ if required == 0 && maximum == 0
65
+ "expected never"
66
+ elsif required == maximum
67
+ "expected exactly #{times(required)}"
68
+ elsif infinite?(maximum)
69
+ "expected at least #{times(required)}"
70
+ elsif required == 0
71
+ "expected at most #{times(maximum)}"
72
+ else
73
+ "expected between #{required} and #{times(maximum)}"
74
+ end
75
+ end
76
+ end
77
+
78
+ protected
79
+
80
+ attr_reader :required, :maximum
81
+
82
+ def times(number)
83
+ number == 1 ? "once" : "#{number} times"
84
+ end
85
+
86
+ def infinite?(number)
87
+ number.respond_to?(:infinite?) && number.infinite?
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -11,18 +11,10 @@ module Mocha
11
11
  def stub(method)
12
12
  unless stubba_methods.include?(method)
13
13
  method.stub
14
- stubba_methods.push method
14
+ stubba_methods.push(method)
15
15
  end
16
16
  end
17
17
 
18
- def verify_all(&block)
19
- unique_mocks.each { |mock| mock.verify(&block) }
20
- end
21
-
22
- def unique_mocks
23
- stubba_methods.inject({}) { |mocks, method| mocks[method.mock.__id__] = method.mock; mocks }.values
24
- end
25
-
26
18
  def unstub_all
27
19
  while stubba_methods.length > 0
28
20
  method = stubba_methods.pop
@@ -0,0 +1,19 @@
1
+ module Mocha
2
+
3
+ class ChangeStateSideEffect
4
+
5
+ def initialize(state)
6
+ @state = state
7
+ end
8
+
9
+ def perform
10
+ @state.activate
11
+ end
12
+
13
+ def mocha_inspect
14
+ "then #{@state.mocha_inspect}"
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -26,19 +26,31 @@ module Mocha
26
26
  end
27
27
 
28
28
  def hide_original_method
29
- stubbee.__metaclass__.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.__metaclass__.method_defined?(method)
29
+ if method_exists?(method)
30
+ begin
31
+ stubbee.__metaclass__.class_eval("alias_method :#{hidden_method}, :#{method}", __FILE__, __LINE__)
32
+ rescue NameError
33
+ # deal with nasties like ActiveRecord::Associations::AssociationProxy
34
+ end
35
+ end
30
36
  end
31
37
 
32
38
  def define_new_method
33
- stubbee.__metaclass__.class_eval "def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end"
39
+ stubbee.__metaclass__.class_eval("def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end", __FILE__, __LINE__)
34
40
  end
35
41
 
36
42
  def remove_new_method
37
- stubbee.__metaclass__.class_eval "remove_method :#{method}"
43
+ stubbee.__metaclass__.class_eval("remove_method :#{method}", __FILE__, __LINE__)
38
44
  end
39
45
 
40
46
  def restore_original_method
41
- stubbee.__metaclass__.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.__metaclass__.method_defined?(hidden_method)
47
+ if method_exists?(hidden_method)
48
+ begin
49
+ stubbee.__metaclass__.class_eval("alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}", __FILE__, __LINE__)
50
+ rescue NameError
51
+ # deal with nasties like ActiveRecord::Associations::AssociationProxy
52
+ end
53
+ end
42
54
  end
43
55
 
44
56
  def hidden_method
@@ -52,7 +64,7 @@ module Mocha
52
64
 
53
65
  def eql?(other)
54
66
  return false unless (other.class == self.class)
55
- (stubbee == other.stubbee) and (method == other.method)
67
+ (stubbee.object_id == other.stubbee.object_id) and (method == other.method)
56
68
  end
57
69
 
58
70
  alias_method :==, :eql?
@@ -60,6 +72,14 @@ module Mocha
60
72
  def to_s
61
73
  "#{stubbee}.#{method}"
62
74
  end
75
+
76
+ def method_exists?(method)
77
+ existing_methods = []
78
+ existing_methods += stubbee.public_methods(true) - stubbee.superclass.public_methods(true)
79
+ existing_methods += stubbee.protected_methods(true) - stubbee.superclass.protected_methods(true)
80
+ existing_methods += stubbee.private_methods(true) - stubbee.superclass.private_methods(true)
81
+ existing_methods.any? { |m| m.to_s == method.to_s }
82
+ end
63
83
 
64
84
  end
65
85