mocha 0.14.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +79 -21
  3. data/RELEASE.md +43 -0
  4. data/bin/build-matrix +71 -0
  5. data/lib/mocha/any_instance_method.rb +2 -2
  6. data/lib/mocha/class_method.rb +5 -2
  7. data/lib/mocha/detection/mini_test.rb +25 -0
  8. data/lib/mocha/detection/test_unit.rb +29 -0
  9. data/lib/mocha/expectation_list.rb +6 -2
  10. data/lib/mocha/integration/mini_test.rb +6 -11
  11. data/lib/mocha/integration/test_unit.rb +4 -9
  12. data/lib/mocha/integration.rb +2 -2
  13. data/lib/mocha/mini_test.rb +3 -0
  14. data/lib/mocha/mock.rb +73 -8
  15. data/lib/mocha/mockery.rb +3 -2
  16. data/lib/mocha/object_methods.rb +4 -2
  17. data/lib/mocha/parameter_matchers/responds_with.rb +1 -1
  18. data/lib/mocha/receivers.rb +49 -0
  19. data/lib/mocha/setup.rb +0 -1
  20. data/lib/mocha/test_unit.rb +3 -0
  21. data/lib/mocha/unexpected_invocation.rb +10 -5
  22. data/lib/mocha/version.rb +1 -1
  23. data/lib/mocha.rb +0 -7
  24. data/test/acceptance/stub_any_instance_method_defined_on_superclass_test.rb +34 -0
  25. data/test/acceptance/stub_any_instance_method_test.rb +1 -0
  26. data/test/acceptance/stub_class_method_defined_on_class_test.rb +3 -0
  27. data/test/acceptance/stub_class_method_defined_on_superclass_test.rb +40 -3
  28. data/test/acceptance/stub_instance_method_defined_on_class_test.rb +3 -0
  29. data/test/acceptance/unexpected_invocation_test.rb +25 -0
  30. data/test/assertions.rb +6 -0
  31. data/test/integration/mini_test_test.rb +4 -18
  32. data/test/integration/test_unit_test.rb +2 -3
  33. data/test/test_helper.rb +6 -2
  34. data/test/test_runner.rb +22 -18
  35. data/test/unit/expectation_list_test.rb +11 -0
  36. data/test/unit/parameter_matchers/responds_with_test.rb +7 -0
  37. data/test/unit/receivers_test.rb +66 -0
  38. data/yard-templates/default/layout/html/google_analytics.erb +9 -9
  39. metadata +91 -107
  40. data/build-matrix.rb +0 -71
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed8646b69b6a5163de10f8bbc6dd5e77b090d451
4
+ data.tar.gz: 42bde3df9467a0a8eac75c3ca816565a9f967feb
5
+ SHA512:
6
+ metadata.gz: 4839f1d90cc07fc20e1662c00a51e6406ac612cbe0c05d25e8abe16a3845eeb8369daf2e516f97e4f8ddd277d93cdd9db47ca9c76f6aa1fab7cbe10ce4b429fd
7
+ data.tar.gz: e36f0c3f28b5d58fbb3467ec305458ef013660df54f465440d5e249160c79ae6b2f622aad0a8d4d2b78272f758726891178267011fa107c59081c89bad42e70b
data/README.md CHANGED
@@ -15,31 +15,65 @@ Install the latest version of the gem with the following command...
15
15
 
16
16
  $ gem install mocha
17
17
 
18
- Note: If you are intending to use Mocha with Test::Unit or MiniTest, you should only load Mocha *after* loading the relevant test library...
18
+ Note: If you are intending to use Mocha with Test::Unit or MiniTest, you should only setup Mocha *after* loading the relevant test library...
19
19
 
20
- require "test/unit"
21
- require "mocha/setup"
20
+ ##### Test::Unit
21
+
22
+ ```ruby
23
+ require 'rubygems'
24
+ gem 'mocha'
25
+ require 'test/unit'
26
+ require 'mocha/test_unit'
27
+ ```
28
+
29
+ ##### MiniTest
30
+
31
+ ```ruby
32
+ require 'rubygems'
33
+ gem 'mocha'
34
+ require 'minitest/unit'
35
+ require 'mocha/mini_test'
36
+ ```
22
37
 
23
38
  #### Bundler
24
39
 
25
- If you're using Bundler, ensure the correct load order by not auto-requiring Mocha in the `Gemfile` and then load it later once you know the test library has been loaded...
40
+ If you're using Bundler, include Mocha in the `Gemfile` and then setup Mocha later once you know the test library has been loaded...
26
41
 
27
- # Gemfile
28
- gem "mocha", :require => false
42
+ ##### Test::Unit
29
43
 
30
- # Elsewhere after Bundler has loaded gems
31
- require "test/unit"
32
- require "mocha/setup"
44
+ ```ruby
45
+ # Gemfile
46
+ gem "mocha"
47
+
48
+ # Elsewhere after Bundler has loaded gems e.g. after `require 'bundler/setup'`
49
+ require "test/unit"
50
+ require "mocha/test_unit"
51
+ ```
52
+
53
+ ##### MiniTest
54
+
55
+ ```ruby
56
+ # Gemfile
57
+ gem "mocha"
58
+
59
+ # Elsewhere after Bundler has loaded gems e.g. after `require 'bundler/setup'`
60
+ require "minitest/unit"
61
+ require "mocha/mini_test"
62
+ ```
33
63
 
34
64
  #### Rails
35
65
 
36
- If you're loading Mocha using Bundler within a Rails application, you should ensure Mocha is not auto-required (as above) and load Mocha manually e.g. at the bottom of your `test_helper.rb`.
66
+ If you're loading Mocha using Bundler within a Rails application, you should setup Mocha manually e.g. at the bottom of your `test_helper.rb`.
67
+
68
+ ##### MiniTest
37
69
 
38
- # Gemfile in Rails app
39
- gem "mocha", :require => false
70
+ ```ruby
71
+ # Gemfile in Rails app
72
+ gem 'mocha'
40
73
 
41
- # At bottom of test_helper.rb
42
- require "mocha/setup"
74
+ # At bottom of test_helper.rb (or at least after `require 'rails/test_help'`)
75
+ require 'mocha/mini_test'
76
+ ```
43
77
 
44
78
  #### Rails Plugin
45
79
 
@@ -47,7 +81,14 @@ Install the Rails plugin...
47
81
 
48
82
  $ rails plugin install git://github.com/freerange/mocha.git
49
83
 
50
- Note: As of version 0.9.8, the Mocha plugin is not automatically loaded at plugin load time. Instead it must be manually loaded e.g. at the bottom of your `test_helper.rb`.
84
+ Note: As of version 0.9.8, the Mocha plugin is not automatically setup at plugin load time. Instead it must be manually setup e.g. at the bottom of your `test_helper.rb`.
85
+
86
+ ##### MiniTest
87
+
88
+ ```ruby
89
+ # At bottom of test_helper.rb (or at least after `require 'rails/test_help'`)
90
+ require 'mocha/mini_test'
91
+ ```
51
92
 
52
93
  #### Know Issues
53
94
 
@@ -62,7 +103,7 @@ Note: As of version 0.9.8, the Mocha plugin is not automatically loaded at plugi
62
103
 
63
104
  ```ruby
64
105
  require 'test/unit'
65
- require 'mocha/setup'
106
+ require 'mocha/test_unit'
66
107
 
67
108
  class MiscExampleTest < Test::Unit::TestCase
68
109
  def test_mocking_a_class_method
@@ -118,7 +159,7 @@ class Enterprise
118
159
  end
119
160
 
120
161
  require 'test/unit'
121
- require 'mocha/setup'
162
+ require 'mocha/test_unit'
122
163
 
123
164
  class EnterpriseTest < Test::Unit::TestCase
124
165
  def test_should_boldly_go
@@ -164,7 +205,7 @@ class Order
164
205
  end
165
206
 
166
207
  require 'test/unit'
167
- require 'mocha/setup'
208
+ require 'mocha/test_unit'
168
209
 
169
210
  class OrderTest < Test::Unit::TestCase
170
211
  # illustrates stubbing instance method
@@ -193,6 +234,18 @@ class OrderTest < Test::Unit::TestCase
193
234
  end
194
235
  ```
195
236
 
237
+ ### Thread safety
238
+
239
+ Mocha is currently *not* thread-safe. There are two main reasons for this: (a) in multi-threaded code Mocha exceptions may be raised in a thread other than the one which is running the test and thus a Mocha exception may not be correctly intercepted by Mocha exception handling code; and (b) partial mocking changes the state of objects in the `ObjectSpace` which is shared across all threads in the Ruby process and this access to what is effectively global state is not synchronized.
240
+
241
+ ### Expectation matching / invocation order
242
+
243
+ Stubs and expectations are basically the same thing. A stub is just an expectation of zero or more invocations. The `Expectation#stubs` method is syntactic sugar to make the intent of the test more explicit.
244
+
245
+ When a method is invoked on a mock object, the mock object searches through its expectations from newest to oldest to find one that matches the invocation. After the invocation, the matching expectation might stop matching further invocations.
246
+
247
+ See the [documentation](http://gofreerange.com/mocha/docs/Mocha/Mock.html) for `Mocha::Mock` for further details.
248
+
196
249
  ### Useful Links
197
250
 
198
251
  * [Official Documentation](http://gofreerange.com/mocha/docs/)
@@ -208,8 +261,13 @@ end
208
261
  ### Contributing
209
262
 
210
263
  * Fork the repository.
211
- * Make your changes in a branch (including tests).
212
- * Ensure all of the tests run against all supported versions of Test::Unit, MiniTest & Ruby by running ./build-matrix.rb (note that currently this makes a *lot* of assumptions about your local environment e.g. rbenv installed with specific Ruby versions).
264
+ * Make your changes in a branch.
265
+ * Add tests for new behaviour. Modify existing tests for changes to existing behaviour.
266
+ * Run `bin/build-matrix` from the root directory and ensure all the tests pass.
267
+ * This script depends on `rbenv` being installed.
268
+ * You must have all the ruby versions listed in `.travis.yml` under the `rvm` key installed (currently 1.8.7, 1.9.3 & 2.0.0).
269
+ * I use `rbenv-aliases` to alias the patch versions.
270
+ * Note that the build matrix takes quite a while to run.
213
271
  * Send us a pull request from your fork/branch.
214
272
 
215
273
  ### Contributors
@@ -218,7 +276,7 @@ See this [list of contributors](https://github.com/freerange/mocha/graphs/contri
218
276
 
219
277
  ### Translations
220
278
 
221
- * [Serbo-Croatian](http://science.webhostinggeeks.com/mocha) by [WHG Team](http://webhostinggeeks.com/).
279
+ * [Serbo-Croatian](http://science.webhostinggeeks.com/mocha) by [WHG Team](http://webhostinggeeks.com/). (may be out-of-date)
222
280
 
223
281
  ### History
224
282
 
data/RELEASE.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Release Notes
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### External changes
6
+ * Assume 'mocha' has been required when requiring 'mocha/setup'.
7
+ * Provide shortcuts for integrating with specific test library i.e. `require 'mocha/test_unit'` or `require 'mocha/mini_test'`
8
+ as alternatives to `require 'mocha/setup'`.
9
+ * Do not automatically try to integrate with test libraries. Since the automatic test library integration functionality
10
+ requires the test library to be loaded and this doesn't usually happen until *after* the bundle is loaded, it makes things
11
+ simpler if we use `require 'mocha/setup'` to explicitly setup Mocha when we know the test library has been loaded. Fixes #146 & #155.
12
+ * Consider stubs on superclasses if none exist on primary receiver. Largely based on changes suggested by @ccutrer in #145.
13
+ Note: this may break existing tests which rely on the old behaviour. Stubbing a superclass method and then invoking that
14
+ method on a child class would previously cause an unexpected invocation error. By searching up through the inheritance
15
+ hierarchy for each of the delegate mock objects, we can provide more intuitive behaviour. Instead of an unexpected invocation
16
+ error, invoking the method on the child class will cause the stubbed method on the superclass to be used.
17
+ * Avoid recursion when constructing unexpected invocation message. Fixes #168.
18
+ * Add explanation of method dispatch. Heavily based on the relevant jMock v1 documentation. Fixes #172.
19
+ * Make class_eval line number more accurate. This sets the line number as the line number of the `def` statement. Closes #169.
20
+ * Allow nesting of `responds_with` parameter matcher. Closes #166.
21
+ * Define `Mocha` module before it's referenced. The test helper defines a class `TestCase` within the `Mocha` module. When
22
+ running the tests inside the bundle, the `Mocha` module happens to be defined at this point. However when running the tests outside the bundle, it is not defined and so an exception is raised: `uninitialized constant Mocha (NameError)`. Fixes #163.
23
+ * Document lack of thread-safety. Fixes #154.
24
+ * Document how to use the build-matrix script. Fixes #160.
25
+ * Stubbing non-public method should use same visibility. This will probably break some existing tests that were somehow relying
26
+ on the stubbed method being public while the original method was protected or private. Fixes #150.
27
+
28
+ ### Internal changes
29
+ * Use lastest Rubygems in Travis CI builds.
30
+ * Run the standard test suite against Ruby 2.1.0 in the build matrix.
31
+ * Run integration tests against Ruby 2.0.0 with latest Test::Unit gem in the build matrix.
32
+ * Test::Unit is not available in Ruby v1.9.3 standard library, so remove it from the build matrix.
33
+ * Force use of Test::Unit runner, etc in relevant integration tests. Prior to this, I don't think we were really testing the
34
+ Mocha integration with Test::Unit much, because, although `TestUnitTest` was a subclass of `Test::Unit::TestCase`, the
35
+ important test case instances are the temporary ones built by `TestRunner#run_as_test` et al. Prior to this change, these
36
+ would only have used Test::Unit where MiniTest was not available *at all* i.e. only in early versions of Ruby and when the
37
+ MiniTest gem was not loaded.
38
+ * Reset environment variables between build matrix builds.
39
+ * Only activate integration with relevant test library for each of the integration tests.
40
+ * Include standard build combinations from Travis CI config i.e. builds using standard library versions of test libraries.
41
+ * Fix `build-matrix.rb` script. Also use `.travis.yml` to decide what combinations to run. This means we
42
+ can now simulate the Travis CI build locally and avoid duplication. Fixes #157.
43
+ * Remove Ruby version map from build matrix script. I'm using the `rbenv-aliases` plugin to alias minor versions to the
44
+ relevant patch version.
45
+
3
46
  ## 0.14.0
4
47
 
5
48
  * Official support for MiniTest v5. All tests now pass on the continuous integration build.
data/bin/build-matrix ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+
5
+ def execute(*commands)
6
+ commands.each do |command|
7
+ system(command)
8
+ unless $?.success?
9
+ message = [
10
+ "Executing shell command failed.",
11
+ " Command: #{command}",
12
+ " Status: #{$?.exitstatus}"
13
+ ].join("\n")
14
+ raise message
15
+ end
16
+ end
17
+ end
18
+
19
+ def reset_bundle
20
+ execute(
21
+ "rm -rf .bundle/gems",
22
+ "rm -rf gemfiles/.bundle/gems",
23
+ "rm -f *.lock",
24
+ "rm -f gemfiles/*.lock"
25
+ )
26
+ end
27
+
28
+ def with_rbenv(command)
29
+ %{export PATH="$HOME/.rbenv/bin:$PATH"; eval "$(rbenv init -)"; #{command}}
30
+ end
31
+
32
+ def run(ruby_version, gemfile, task = "test")
33
+ ENV["RBENV_VERSION"] = ruby_version
34
+ ENV["BUNDLE_GEMFILE"] = gemfile
35
+ ENV["MOCHA_OPTIONS"] = "debug"
36
+ ENV["MOCHA_NO_DOCS"] = "true"
37
+ reset_bundle
38
+ execute(
39
+ with_rbenv("bundle install --gemfile=#{gemfile}"),
40
+ with_rbenv("bundle exec rake #{task}"),
41
+ )
42
+ end
43
+
44
+ travis_config = YAML.load(File.read('.travis.yml'))
45
+ build_configs = travis_config['matrix']['include']
46
+ travis_config['rvm'].each do |ruby_version|
47
+ travis_config['gemfile'].each do |gemfile|
48
+ travis_config['env'].each do |env|
49
+ build_configs << { 'rvm' => ruby_version, 'gemfile' => gemfile, 'env' => env }
50
+ end
51
+ end
52
+ end
53
+
54
+ build_configs.each do |config|
55
+ ruby_version = config['rvm']
56
+ gemfile = config['gemfile']
57
+ environment_variables = Hash[*config['env'].split.flat_map { |e| e.split('=') }]
58
+ original_environment_variables = {}
59
+ begin
60
+ environment_variables.each do |k, v|
61
+ original_environment_variables[k] = ENV[k]
62
+ ENV[k] = v
63
+ end
64
+ p [ruby_version, gemfile, environment_variables]
65
+ run(ruby_version, gemfile)
66
+ ensure
67
+ original_environment_variables.each do |k, v|
68
+ ENV[k] = v
69
+ end
70
+ end
71
+ end
@@ -32,11 +32,11 @@ module Mocha
32
32
  end
33
33
 
34
34
  def define_new_method
35
- stubbee.class_eval(%{
35
+ stubbee.class_eval(<<-CODE, __FILE__, __LINE__ + 1)
36
36
  def #{method}(*args, &block)
37
37
  self.class.any_instance.mocha.method_missing(:#{method}, *args, &block)
38
38
  end
39
- }, __FILE__, __LINE__)
39
+ CODE
40
40
  end
41
41
 
42
42
  def remove_new_method
@@ -54,11 +54,14 @@ module Mocha
54
54
  end
55
55
 
56
56
  def define_new_method
57
- stubbee.__metaclass__.class_eval(%{
57
+ stubbee.__metaclass__.class_eval(<<-CODE, __FILE__, __LINE__ + 1)
58
58
  def #{method}(*args, &block)
59
59
  mocha.method_missing(:#{method}, *args, &block)
60
60
  end
61
- }, __FILE__, __LINE__)
61
+ CODE
62
+ if @original_visibility
63
+ Module.instance_method(@original_visibility).bind(stubbee.__metaclass__).call(method)
64
+ end
62
65
  end
63
66
 
64
67
  def remove_new_method
@@ -0,0 +1,25 @@
1
+ module Mocha
2
+ module Detection
3
+ module MiniTest
4
+ def self.testcase
5
+ if defined?(::Minitest::Test)
6
+ ::Minitest::Test
7
+ elsif defined?(::MiniTest::Unit::TestCase)
8
+ ::MiniTest::Unit::TestCase
9
+ else
10
+ nil
11
+ end
12
+ end
13
+
14
+ def self.version
15
+ if defined?(::MiniTest::Unit::VERSION)
16
+ ::MiniTest::Unit::VERSION
17
+ elsif defined?(::Minitest::VERSION)
18
+ ::Minitest::VERSION
19
+ else
20
+ '0.0.0'
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ module Mocha
2
+ module Detection
3
+ module TestUnit
4
+ def self.testcase
5
+ if defined?(::Test::Unit::TestCase) &&
6
+ !(defined?(::MiniTest::Unit::TestCase) && (::Test::Unit::TestCase < ::MiniTest::Unit::TestCase)) &&
7
+ !(defined?(::MiniTest::Spec) && (::Test::Unit::TestCase < ::MiniTest::Spec))
8
+ ::Test::Unit::TestCase
9
+ else
10
+ nil
11
+ end
12
+ end
13
+
14
+ def self.version
15
+ version = '1.0.0'
16
+ if testcase
17
+ begin
18
+ require 'test/unit/version'
19
+ rescue LoadError
20
+ end
21
+ if defined?(::Test::Unit::VERSION)
22
+ version = ::Test::Unit::VERSION
23
+ end
24
+ end
25
+ version
26
+ end
27
+ end
28
+ end
29
+ end
@@ -2,8 +2,8 @@ module Mocha
2
2
 
3
3
  class ExpectationList
4
4
 
5
- def initialize
6
- @expectations = []
5
+ def initialize(expectations = [])
6
+ @expectations = expectations
7
7
  end
8
8
 
9
9
  def add(expectation)
@@ -47,6 +47,10 @@ module Mocha
47
47
  @expectations.any?
48
48
  end
49
49
 
50
+ def +(other)
51
+ self.class.new(self.to_a + other.to_a)
52
+ end
53
+
50
54
  private
51
55
 
52
56
  def matching_expectations(method_name, *arguments)
@@ -1,5 +1,7 @@
1
1
  require 'mocha/debug'
2
2
 
3
+ require 'mocha/detection/mini_test'
4
+
3
5
  require 'mocha/integration/mini_test/nothing'
4
6
  require 'mocha/integration/mini_test/version_13'
5
7
  require 'mocha/integration/mini_test/version_140'
@@ -16,13 +18,8 @@ module Mocha
16
18
  module Integration
17
19
  module MiniTest
18
20
  def self.activate
19
- return false unless defined?(::MiniTest::Unit::TestCase)
20
-
21
- mini_test_version = begin
22
- Gem::Version.new(::MiniTest::Unit::VERSION)
23
- rescue LoadError
24
- Gem::Version.new('0.0.0')
25
- end
21
+ return false unless Detection::MiniTest.testcase
22
+ mini_test_version = Gem::Version.new(Detection::MiniTest.version)
26
23
 
27
24
  Debug.puts "Detected MiniTest version: #{mini_test_version}"
28
25
 
@@ -40,15 +37,13 @@ module Mocha
40
37
  MiniTest::Nothing
41
38
  ].detect { |m| m.applicable_to?(mini_test_version) }
42
39
 
43
- target = defined?(Minitest::Test) ? ::Minitest::Test : ::MiniTest::Unit::TestCase
40
+ target = Detection::MiniTest.testcase
44
41
  unless target < integration_module
45
42
  Debug.puts "Applying #{integration_module.description}"
46
43
  target.send(:include, integration_module)
47
44
  end
45
+ true
48
46
  end
49
- true
50
47
  end
51
48
  end
52
49
  end
53
-
54
-
@@ -1,5 +1,7 @@
1
1
  require 'mocha/debug'
2
2
 
3
+ require 'mocha/detection/test_unit'
4
+
3
5
  require 'mocha/integration/test_unit/nothing'
4
6
  require 'mocha/integration/test_unit/ruby_version_185_and_below'
5
7
  require 'mocha/integration/test_unit/ruby_version_186_and_above'
@@ -13,15 +15,8 @@ module Mocha
13
15
  module Integration
14
16
  module TestUnit
15
17
  def self.activate
16
- return false unless defined?(::Test::Unit::TestCase) && !(defined?(::MiniTest::Unit::TestCase) && (::Test::Unit::TestCase < ::MiniTest::Unit::TestCase)) && !(defined?(::MiniTest::Spec) && (::Test::Unit::TestCase < ::MiniTest::Spec))
17
-
18
- test_unit_version = begin
19
- require 'test/unit/version'
20
- Gem::Version.new(::Test::Unit::VERSION)
21
- rescue LoadError
22
- Gem::Version.new('1.0.0')
23
- end
24
-
18
+ return false unless Detection::TestUnit.testcase
19
+ test_unit_version = Gem::Version.new(Detection::TestUnit.version)
25
20
  ruby_version = Gem::Version.new(RUBY_VERSION.dup)
26
21
 
27
22
  Debug.puts "Detected Ruby version: #{ruby_version}"
@@ -6,8 +6,8 @@ module Mocha
6
6
  module Integration
7
7
  def self.activate
8
8
  if [Integration::TestUnit, Integration::MiniTest].map(&:activate).none?
9
- Deprecation.warning("Test::Unit or MiniTest must be loaded *before* Mocha.")
10
- Deprecation.warning("If you're integrating with a test library other than Test::Unit or MiniTest, you should use `require 'mocha/api'` instead of `require 'mocha'`.")
9
+ Deprecation.warning("Test::Unit or MiniTest must be loaded *before* `require 'mocha/setup'`.")
10
+ Deprecation.warning("If you're integrating with a test library other than Test::Unit or MiniTest, you should use `require 'mocha/api'` instead of `require 'mocha/setup'`.")
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,3 @@
1
+ require "mocha/integration/mini_test"
2
+
3
+ Mocha::Integration::MiniTest.activate
data/lib/mocha/mock.rb CHANGED
@@ -2,6 +2,7 @@ require 'metaclass'
2
2
  require 'mocha/expectation'
3
3
  require 'mocha/expectation_list'
4
4
  require 'mocha/names'
5
+ require 'mocha/receivers'
5
6
  require 'mocha/method_matcher'
6
7
  require 'mocha/parameters_matcher'
7
8
  require 'mocha/unexpected_invocation'
@@ -12,7 +13,59 @@ module Mocha
12
13
 
13
14
  # Traditional mock object.
14
15
  #
15
- # All methods return an {Expectation} which can be further modified by methods on {Expectation}.
16
+ # All methods return an {Expectation} which can be further modified by
17
+ # methods on {Expectation}.
18
+ #
19
+ # Stubs and expectations are basically the same thing. A stub is just an
20
+ # expectation of zero or more invocations. The {#stubs} method is syntactic
21
+ # sugar to make the intent of the test more explicit.
22
+ #
23
+ # When a method is invoked on a mock object, the mock object searches through
24
+ # its expectations from newest to oldest to find one that matches the
25
+ # invocation. After the invocation, the matching expectation might stop
26
+ # matching further invocations. For example, an +expects(:foo).once+
27
+ # expectation only matches once and will be ignored on future invocations
28
+ # while an +expects(:foo).at_least_once+ expectation will always be matched
29
+ # against invocations.
30
+ #
31
+ # This scheme allows you to:
32
+ #
33
+ # - Set up default stubs in your the +setup+ method of your test class and
34
+ # override some of those stubs in individual tests.
35
+ # - Set up different +once+ expectations for the same method with different
36
+ # action per invocation. However, it's better to use the
37
+ # {Expectation#returns} method with multiple arguments to do this, as
38
+ # described below.
39
+ #
40
+ # However, there are some possible "gotchas" caused by this scheme:
41
+ #
42
+ # - if you create an expectation and then a stub for the same method, the
43
+ # stub will always override the expectation and the expectation will never
44
+ # be met.
45
+ # - if you create a stub and then an expectation for the same method, the
46
+ # expectation will match, and when it stops matching the stub will be used
47
+ # instead, possibly masking test failures.
48
+ # - if you create different expectations for the same method, they will be
49
+ # invoked in the opposite order than that in which they were specified,
50
+ # rather than the same order.
51
+ #
52
+ # The best thing to do is not set up multiple expectations and stubs for the
53
+ # same method with exactly the same matchers. Instead, use the
54
+ # {Expectation#returns} method with multiple arguments to create multiple
55
+ # actions for a method. You can also chain multiple calls to
56
+ # {Expectation#returns} and {Expectation#raises} (along with syntactic sugar
57
+ # {Expectation#then} if desired).
58
+ #
59
+ # @example
60
+ # object = mock()
61
+ # object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
62
+ # object.expected_method # => 1
63
+ # object.expected_method # => 2
64
+ # object.expected_method # => raises exception of class Exception1
65
+ #
66
+ # If you want to specify more complex ordering or order invocations across
67
+ # different mock objects, use the {Expectation#in_sequence} method to
68
+ # explicitly define a total or partial ordering of invocations.
16
69
  class Mock
17
70
 
18
71
  # Adds an expectation that the specified method must be called exactly once with any parameters.
@@ -194,12 +247,14 @@ module Mocha
194
247
  end
195
248
 
196
249
  # @private
197
- def initialize(mockery, name = nil, &block)
250
+ def initialize(mockery, name = nil, receiver = nil, &block)
198
251
  @mockery = mockery
199
252
  @name = name || DefaultName.new(self)
253
+ @receiver = receiver || DefaultReceiver.new(self)
200
254
  @expectations = ExpectationList.new
201
255
  @everything_stubbed = false
202
256
  @responder = nil
257
+ @unexpected_invocation = nil
203
258
  instance_eval(&block) if block
204
259
  end
205
260
 
@@ -223,18 +278,28 @@ module Mocha
223
278
  @everything_stubbed = true
224
279
  end
225
280
 
281
+ # @private
282
+ def all_expectations
283
+ @receiver.mocks.inject(ExpectationList.new) { |e, m| e + m.__expectations__ }
284
+ end
285
+
226
286
  # @private
227
287
  def method_missing(symbol, *arguments, &block)
228
288
  if @responder and not @responder.respond_to?(symbol)
229
289
  raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
230
290
  end
231
- if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
291
+ if matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(symbol, *arguments)
232
292
  matching_expectation_allowing_invocation.invoke(&block)
233
293
  else
234
- if (matching_expectation = @expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
235
- matching_expectation.invoke(&block) if matching_expectation
236
- message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
237
- message << @mockery.mocha_inspect
294
+ if (matching_expectation = all_expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
295
+ if @unexpected_invocation.nil?
296
+ @unexpected_invocation = UnexpectedInvocation.new(self, symbol, *arguments)
297
+ matching_expectation.invoke(&block) if matching_expectation
298
+ message = @unexpected_invocation.full_description
299
+ message << @mockery.mocha_inspect
300
+ else
301
+ message = @unexpected_invocation.short_description
302
+ end
238
303
  raise ExpectationErrorFactory.build(message, caller)
239
304
  end
240
305
  end
@@ -249,7 +314,7 @@ module Mocha
249
314
  @responder.respond_to?(symbol)
250
315
  end
251
316
  else
252
- @everything_stubbed || @expectations.matches_method?(symbol)
317
+ @everything_stubbed || all_expectations.matches_method?(symbol)
253
318
  end
254
319
  end
255
320
 
data/lib/mocha/mockery.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'mocha/central'
2
2
  require 'mocha/mock'
3
3
  require 'mocha/names'
4
+ require 'mocha/receivers'
4
5
  require 'mocha/state_machine'
5
6
  require 'mocha/logger'
6
7
  require 'mocha/configuration'
@@ -40,11 +41,11 @@ module Mocha
40
41
  end
41
42
 
42
43
  def mock_impersonating(object, &block)
43
- add_mock(Mock.new(self, ImpersonatingName.new(object), &block))
44
+ add_mock(Mock.new(self, ImpersonatingName.new(object), ObjectReceiver.new(object), &block))
44
45
  end
45
46
 
46
47
  def mock_impersonating_any_instance_of(klass, &block)
47
- add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), &block))
48
+ add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), AnyInstanceReceiver.new(klass), &block))
48
49
  end
49
50
 
50
51
  def new_state_machine(name)
@@ -35,7 +35,7 @@ module Mocha
35
35
 
36
36
  # Adds an expectation that the specified method must be called exactly once with any parameters.
37
37
  #
38
- # The original implementation of the method is replaced during the test and then restored at the end of the test.
38
+ # The original implementation of the method is replaced during the test and then restored at the end of the test. The temporary replacement method has the same visibilty as the original method.
39
39
  #
40
40
  # @param [Symbol,String] method_name name of expected method
41
41
  # @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
@@ -84,6 +84,8 @@ module Mocha
84
84
 
85
85
  # Adds an expectation that the specified method may be called any number of times with any parameters.
86
86
  #
87
+ # The original implementation of the method is replaced during the test and then restored at the end of the test. The temporary replacement method has the same visibilty as the original method.
88
+ #
87
89
  # @param [Symbol,String] method_name name of stubbed method
88
90
  # @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
89
91
  #
@@ -128,7 +130,7 @@ module Mocha
128
130
 
129
131
  # Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
130
132
  #
131
- # Restores the original behaviour of the methods before they were stubbed.
133
+ # Restores the original behaviour of the methods before they were stubbed. This is normally done automatically at the end of each test, but in some circumstances you may want to do it *before* the end of the test.
132
134
  #
133
135
  # WARNING: If you {#unstub} a method which still has unsatisfied expectations, you may be removing the only way those expectations can be satisfied. Use {#unstub} with care.
134
136
  #