rspec-mocks 2.13.1 → 2.14.0.rc1

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 (99) hide show
  1. data/Changelog.md +45 -4
  2. data/README.md +1 -1
  3. data/features/argument_matchers/README.md +2 -2
  4. data/features/argument_matchers/explicit.feature +2 -3
  5. data/features/argument_matchers/general_matchers.feature +2 -2
  6. data/features/argument_matchers/type_matchers.feature +3 -4
  7. data/features/message_expectations/README.md +2 -2
  8. data/features/message_expectations/any_instance.feature +2 -2
  9. data/features/message_expectations/block_local_expectations.feature.pending +3 -3
  10. data/features/message_expectations/expect_message_using_expect.feature +103 -0
  11. data/features/message_expectations/expect_message_using_should_receive.feature +118 -0
  12. data/features/message_expectations/receive_counts.feature +1 -1
  13. data/features/method_stubs/README.md +1 -1
  14. data/features/method_stubs/any_instance.feature +11 -11
  15. data/features/method_stubs/as_null_object.feature +1 -1
  16. data/features/method_stubs/stub_implementation.feature +2 -2
  17. data/features/outside_rspec/configuration.feature +0 -20
  18. data/features/spies/spy_partial_mock_method.feature +34 -0
  19. data/features/spies/spy_pure_mock_method.feature +76 -0
  20. data/features/spies/spy_unstubbed_method.feature +18 -0
  21. data/features/step_definitions/additional_cli_steps.rb +7 -0
  22. data/features/test_frameworks/test_unit.feature +43 -0
  23. data/lib/rspec/mocks.rb +9 -34
  24. data/lib/rspec/mocks/any_instance/chain.rb +8 -2
  25. data/lib/rspec/mocks/any_instance/expectation_chain.rb +19 -16
  26. data/lib/rspec/mocks/any_instance/recorder.rb +6 -3
  27. data/lib/rspec/mocks/any_instance/stub_chain.rb +11 -11
  28. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +8 -10
  29. data/lib/rspec/mocks/argument_list_matcher.rb +7 -3
  30. data/lib/rspec/mocks/configuration.rb +28 -1
  31. data/lib/rspec/mocks/deprecation.rb +18 -0
  32. data/lib/rspec/mocks/error_generator.rb +60 -8
  33. data/lib/rspec/mocks/errors.rb +1 -1
  34. data/lib/rspec/mocks/example_methods.rb +39 -3
  35. data/lib/rspec/mocks/extensions/marshal.rb +4 -10
  36. data/lib/rspec/mocks/framework.rb +16 -4
  37. data/lib/rspec/mocks/instance_method_stasher.rb +3 -0
  38. data/lib/rspec/mocks/matchers/have_received.rb +93 -0
  39. data/lib/rspec/mocks/matchers/receive.rb +92 -0
  40. data/lib/rspec/mocks/message_expectation.rb +66 -129
  41. data/lib/rspec/mocks/method_double.rb +50 -43
  42. data/lib/rspec/mocks/mutate_const.rb +8 -20
  43. data/lib/rspec/mocks/proxy.rb +41 -25
  44. data/lib/rspec/mocks/proxy_for_nil.rb +36 -0
  45. data/lib/rspec/mocks/space.rb +64 -11
  46. data/lib/rspec/mocks/stub_chain.rb +51 -0
  47. data/lib/rspec/mocks/syntax.rb +329 -0
  48. data/lib/rspec/mocks/targets.rb +69 -0
  49. data/lib/rspec/mocks/test_double.rb +25 -4
  50. data/lib/rspec/mocks/version.rb +1 -1
  51. data/lib/spec/mocks.rb +1 -3
  52. data/spec/rspec/mocks/and_call_original_spec.rb +8 -0
  53. data/spec/rspec/mocks/and_yield_spec.rb +6 -6
  54. data/spec/rspec/mocks/any_instance_spec.rb +43 -31
  55. data/spec/rspec/mocks/any_number_of_times_spec.rb +6 -0
  56. data/spec/rspec/mocks/argument_expectation_spec.rb +12 -14
  57. data/spec/rspec/mocks/at_least_spec.rb +46 -37
  58. data/spec/rspec/mocks/at_most_spec.rb +12 -12
  59. data/spec/rspec/mocks/block_return_value_spec.rb +18 -1
  60. data/spec/rspec/mocks/bug_report_10260_spec.rb +1 -1
  61. data/spec/rspec/mocks/bug_report_10263_spec.rb +1 -1
  62. data/spec/rspec/mocks/bug_report_11545_spec.rb +4 -4
  63. data/spec/rspec/mocks/bug_report_600_spec.rb +1 -1
  64. data/spec/rspec/mocks/bug_report_7611_spec.rb +1 -1
  65. data/spec/rspec/mocks/configuration_spec.rb +124 -0
  66. data/spec/rspec/mocks/double_spec.rb +13 -1
  67. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +17 -1
  68. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +13 -13
  69. data/spec/rspec/mocks/matchers/have_received_spec.rb +266 -0
  70. data/spec/rspec/mocks/matchers/receive_spec.rb +318 -0
  71. data/spec/rspec/mocks/methods_spec.rb +27 -0
  72. data/spec/rspec/mocks/mock_ordering_spec.rb +4 -4
  73. data/spec/rspec/mocks/mock_space_spec.rb +94 -39
  74. data/spec/rspec/mocks/mock_spec.rb +65 -50
  75. data/spec/rspec/mocks/multiple_return_value_spec.rb +10 -10
  76. data/spec/rspec/mocks/mutate_const_spec.rb +21 -1
  77. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +10 -4
  78. data/spec/rspec/mocks/null_object_mock_spec.rb +11 -2
  79. data/spec/rspec/mocks/once_counts_spec.rb +5 -5
  80. data/spec/rspec/mocks/options_hash_spec.rb +4 -4
  81. data/spec/rspec/mocks/partial_mock_spec.rb +20 -11
  82. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +7 -7
  83. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +2 -2
  84. data/spec/rspec/mocks/precise_counts_spec.rb +6 -6
  85. data/spec/rspec/mocks/serialization_spec.rb +1 -22
  86. data/spec/rspec/mocks/stash_spec.rb +4 -12
  87. data/spec/rspec/mocks/stub_implementation_spec.rb +3 -3
  88. data/spec/rspec/mocks/stub_spec.rb +44 -20
  89. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +6 -6
  90. data/spec/rspec/mocks/twice_counts_spec.rb +6 -6
  91. data/spec/rspec/mocks_spec.rb +1 -3
  92. data/spec/spec_helper.rb +25 -1
  93. metadata +86 -81
  94. data/features/message_expectations/expect_message.feature +0 -94
  95. data/lib/rspec/mocks/any_instance.rb +0 -81
  96. data/lib/rspec/mocks/extensions/psych.rb +0 -23
  97. data/lib/rspec/mocks/methods.rb +0 -155
  98. data/lib/rspec/mocks/serialization.rb +0 -34
  99. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +0 -197
@@ -6,7 +6,7 @@ Feature: as_null_object
6
6
  EXCEPTION: `to_ary` will raise a NoMethodError unless explicitly stubbed in
7
7
  order to support `flatten` on an Array containing a double.
8
8
 
9
- Scenario: double acting as_null_object
9
+ Scenario: double acting as_null_object
10
10
  Given a file named "as_null_object_spec.rb" with:
11
11
  """ruby
12
12
  describe "a double with as_null_object called" do
@@ -2,7 +2,7 @@ Feature: stub with substitute implementation
2
2
 
3
3
  You can stub an implementation of a method (a.k.a. fake) by passing a block
4
4
  to the `stub` method.
5
-
5
+
6
6
  Scenario: stub implementation
7
7
  Given a file named "stub_implementation_spec.rb" with:
8
8
  """ruby
@@ -16,7 +16,7 @@ Feature: stub with substitute implementation
16
16
  "got that"
17
17
  end
18
18
  end
19
-
19
+
20
20
  object.foo(:this).should eq("got this")
21
21
  object.foo(:that).should eq("got that")
22
22
  end
@@ -15,10 +15,6 @@ Feature: configure any test framework to use rspec-mocks
15
15
  should_receive
16
16
  should_not_receive
17
17
  stub
18
-
19
- In order to give control to the consuming framework, none of these facilities
20
- are added until RSpec::Mocks::setup(self) is called. Simply requiring
21
- 'rspec/mocks' is not sufficient.
22
18
 
23
19
  NOTICE: the stub() method that is added to the object passed to setup is not
24
20
  the same stub() method that is added to every other object.
@@ -64,19 +60,3 @@ Feature: configure any test framework to use rspec-mocks
64
60
  Then the output should contain "true"
65
61
  But the output should not contain "false"
66
62
 
67
- Scenario: require "rspec/mocks" does not add methods to Object
68
- Given a file named "foo.rb" with:
69
- """ruby
70
- require 'rspec/mocks'
71
-
72
- obj = Object.new
73
-
74
- puts obj.respond_to?(:should_receive)
75
- puts obj.respond_to?(:should_not_receive)
76
- puts obj.respond_to?(:stub)
77
- """
78
-
79
- When I run `ruby foo.rb`
80
- Then the output should contain "false"
81
- But the output should not contain "true"
82
-
@@ -0,0 +1,34 @@
1
+ Feature: Spy on a stubbed method on a partial mock
2
+
3
+ You can also use `have_received` to verify that a stubbed method was invoked
4
+ on a partial mock.
5
+
6
+ Scenario: verify a stubbed method
7
+ Given a file named "verified_spy_spec.rb" with:
8
+ """ruby
9
+ describe "have_received" do
10
+ it "passes when the expectation is met" do
11
+ invitation = Object.new
12
+ invitation.stub(:deliver => true)
13
+ invitation.deliver
14
+ invitation.should have_received(:deliver)
15
+ end
16
+ end
17
+ """
18
+ When I run `rspec verified_spy_spec.rb`
19
+ Then the examples should all pass
20
+
21
+ Scenario: fail to verify a stubbed method
22
+ Given a file named "failed_spy_spec.rb" with:
23
+ """ruby
24
+ describe "have_received" do
25
+ it "fails when the expectation is not met" do
26
+ invitation = Object.new
27
+ invitation.stub(:deliver => true)
28
+ invitation.should have_received(:deliver)
29
+ end
30
+ end
31
+ """
32
+ When I run `rspec failed_spy_spec.rb`
33
+ Then the output should contain "expected: 1 time"
34
+ And the output should contain "received: 0 times"
@@ -0,0 +1,76 @@
1
+ Feature: Spy on a stubbed method on a pure mock
2
+
3
+ You can use `have_received` to verify that a stubbed method was invoked,
4
+ rather than setting an expectation for it to be invoked beforehand.
5
+
6
+ Scenario: verify a stubbed method
7
+ Given a file named "verified_spy_spec.rb" with:
8
+ """ruby
9
+ describe "have_received" do
10
+ it "passes when the expectation is met" do
11
+ invitation = double('invitation', :deliver => true)
12
+ invitation.deliver
13
+ invitation.should have_received(:deliver)
14
+ end
15
+ end
16
+ """
17
+ When I run `rspec verified_spy_spec.rb`
18
+ Then the examples should all pass
19
+
20
+ Scenario: verify a stubbed method with message expectations
21
+ Given a file named "verified_message_expectations_spec.rb" with:
22
+ """ruby
23
+ describe "have_received" do
24
+ it "passes when the expectation is met" do
25
+ invitation = double('invitation', :deliver => true)
26
+ 2.times { invitation.deliver(:expected, :arguments) }
27
+ invitation.should have_received(:deliver).
28
+ with(:expected, :arguments).
29
+ twice
30
+ end
31
+ end
32
+ """
33
+ When I run `rspec verified_message_expectations_spec.rb`
34
+ Then the examples should all pass
35
+
36
+ Scenario: fail to verify a stubbed method
37
+ Given a file named "failed_spy_spec.rb" with:
38
+ """ruby
39
+ describe "have_received" do
40
+ it "fails when the expectation is not met" do
41
+ invitation = double('invitation', :deliver => true)
42
+ invitation.should have_received(:deliver)
43
+ end
44
+ end
45
+ """
46
+ When I run `rspec failed_spy_spec.rb`
47
+ Then the output should contain "expected: 1 time"
48
+ And the output should contain "received: 0 times"
49
+
50
+ Scenario: fail to verify message expectations
51
+ Given a file named "failed_message_expectations_spec.rb" with:
52
+ """ruby
53
+ describe "have_received" do
54
+ it "fails when the arguments are different" do
55
+ invitation = double('invitation', :deliver => true)
56
+ invitation.deliver(:unexpected)
57
+ invitation.should have_received(:deliver).with(:expected, :arguments)
58
+ end
59
+ end
60
+ """
61
+ When I run `rspec failed_message_expectations_spec.rb`
62
+ Then the output should contain "expected: (:expected, :arguments)"
63
+ And the output should contain "got: (:unexpected)"
64
+
65
+ Scenario: generate a spy message
66
+ Given a file named "spy_message_spec.rb" with:
67
+ """ruby
68
+ describe "have_received" do
69
+ subject(:invitation) { double('invitation', :deliver => true) }
70
+ before { invitation.deliver }
71
+
72
+ it { should have_received(:deliver) }
73
+ end
74
+ """
75
+ When I run `rspec --format documentation spy_message_spec.rb`
76
+ Then the output should contain "should have received deliver"
@@ -0,0 +1,18 @@
1
+ Feature: Spy on an unstubbed method
2
+
3
+ Using have_received on an unstubbed method will never pass, so rspec-mocks
4
+ issues a helpful error message.
5
+
6
+ Scenario: fail to verify a stubbed method
7
+ Given a file named "failed_spy_spec.rb" with:
8
+ """ruby
9
+ describe "have_received" do
10
+ it "raises a helpful error for unstubbed methods" do
11
+ object = Object.new
12
+ object.object_id
13
+ object.should have_received(:object_id)
14
+ end
15
+ end
16
+ """
17
+ When I run `rspec failed_spy_spec.rb`
18
+ Then the output should contain "that method has not been stubbed"
@@ -2,3 +2,10 @@ Then /^the example(?:s)? should(?: all)? pass$/ do
2
2
  step %q{the output should contain "0 failures"}
3
3
  step %q{the exit status should be 0}
4
4
  end
5
+
6
+ # Useful for when the output is slightly different on different versions of ruby
7
+ Then /^the output should contain "([^"]*)" or "([^"]*)"$/ do |string1, string2|
8
+ unless [string1, string2].any? { |s| all_output =~ regexp(s) }
9
+ fail %Q{Neither "#{string1}" or "#{string2}" were found in:\n#{all_output}}
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ Feature: Test::Unit integration
2
+
3
+ rspec-mocks is a stand-alone gem that can be used without the rest of
4
+ RSpec. If you like the way Test::Unit (or MiniTest) organizes tests, but
5
+ prefer RSpec's approach to mocking/stubbing/doubles etc, you can have both.
6
+
7
+ The one downside is that failures are reported as errors with MiniTest.
8
+
9
+ Scenario: use rspec/mocks with Test::Unit
10
+ Given a file named "rspec_mocks_test.rb" with:
11
+ """ruby
12
+ require 'test/unit'
13
+ require 'rspec/mocks'
14
+
15
+ class RSpecMocksTest < Test::Unit::TestCase
16
+ def setup
17
+ RSpec::Mocks.setup(Object)
18
+ RSpec::Mocks.setup(self)
19
+ end
20
+
21
+ def test_passing_expectation
22
+ obj = Object.new
23
+ expect(obj).to receive(:message)
24
+ obj.message
25
+ end
26
+
27
+ def test_failing_expectation
28
+ obj = Object.new
29
+ expect(obj).to_not receive(:message)
30
+ obj.message
31
+ end
32
+
33
+ def test_with_deprecation_warning
34
+ obj = Object.new
35
+ obj.stub(:old_message) { RSpec.deprecate(:old_message, :replacement => :message) }
36
+ obj.old_message
37
+ end
38
+ end
39
+ """
40
+ When I run `ruby rspec_mocks_test.rb`
41
+ Then the output should contain "3 tests, 0 assertions, 0 failures, 1 errors" or "3 tests, 0 assertions, 1 failures, 0 errors"
42
+ And the output should contain "expected: 0 times with any arguments"
43
+ And the output should contain "old_message is deprecated. Use message instead."
@@ -1,14 +1,13 @@
1
1
  require 'rspec/mocks/framework'
2
2
  require 'rspec/mocks/version'
3
- require 'rspec/mocks/example_methods'
4
3
 
5
4
  module RSpec
5
+
6
6
  module Mocks
7
7
  class << self
8
8
  attr_accessor :space
9
9
 
10
10
  def setup(host)
11
- add_extensions unless extensions_added?
12
11
  (class << host; self; end).class_eval do
13
12
  include RSpec::Mocks::ExampleMethods
14
13
  end
@@ -23,16 +22,12 @@ module RSpec
23
22
  space.reset_all
24
23
  end
25
24
 
26
- def configuration
27
- @configuration ||= Configuration.new
25
+ def proxy_for(object)
26
+ space.proxy_for(object)
28
27
  end
29
28
 
30
- # @api private
31
- # Used internally by RSpec to display custom deprecation warnings. This
32
- # is also defined in rspec-core, but we can't assume it's loaded since
33
- # rspec-expectations should be usable w/o rspec-core.
34
- def warn_deprecation(message)
35
- warn(message)
29
+ def any_instance_recorder_for(klass)
30
+ space.any_instance_recorder_for(klass)
36
31
  end
37
32
 
38
33
  # @api private
@@ -53,30 +48,10 @@ module RSpec
53
48
  object.method(method_name)
54
49
  end
55
50
  end
56
-
57
- private
58
-
59
- def add_extensions
60
- method_host.class_eval { include RSpec::Mocks::Methods }
61
- Class.class_eval { include RSpec::Mocks::AnyInstance }
62
- $_rspec_mocks_extensions_added = true
63
- end
64
-
65
- def extensions_added?
66
- defined?($_rspec_mocks_extensions_added)
67
- end
68
-
69
- def method_host
70
- # On 1.8.7, Object.ancestors.last == Kernel but
71
- # things blow up if we include `RSpec::Mocks::Methods`
72
- # into Kernel...not sure why.
73
- return Object unless defined?(::BasicObject)
74
-
75
- # MacRuby has BasicObject but it's not the root class.
76
- return Object unless Object.ancestors.last == ::BasicObject
77
-
78
- ::BasicObject
79
- end
80
51
  end
52
+
53
+ # @private
54
+ IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
81
55
  end
82
56
  end
57
+
@@ -2,6 +2,11 @@ module RSpec
2
2
  module Mocks
3
3
  module AnyInstance
4
4
  class Chain
5
+ def initialize(*args, &block)
6
+ @expectation_args = args
7
+ @expectation_block = block
8
+ end
9
+
5
10
  class << self
6
11
  private
7
12
 
@@ -38,8 +43,9 @@ module RSpec
38
43
 
39
44
  # @private
40
45
  def playback!(instance)
41
- messages.inject(instance) do |_instance, message|
42
- _instance.__send__(*message.first, &message.last)
46
+ message_expectation = create_message_expectation_on(instance)
47
+ messages.inject(message_expectation) do |object, message|
48
+ object.__send__(*message.first, &message.last)
43
49
  end
44
50
  end
45
51
 
@@ -8,8 +8,8 @@ module RSpec
8
8
  end
9
9
 
10
10
  def initialize(*args, &block)
11
- record(*args, &block)
12
11
  @expectation_fulfilled = false
12
+ super
13
13
  end
14
14
 
15
15
  private
@@ -19,28 +19,26 @@ module RSpec
19
19
 
20
20
  # @api private
21
21
  class PositiveExpectationChain < ExpectationChain
22
- def initialize(*args, &block)
23
- super(:should_receive, *args, &block)
24
- end
25
22
 
26
23
  private
27
24
 
25
+ def create_message_expectation_on(instance)
26
+ proxy = ::RSpec::Mocks.proxy_for(instance)
27
+ expected_from = IGNORED_BACKTRACE_LINE
28
+ proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
29
+ end
30
+
28
31
  def invocation_order
29
32
  @invocation_order ||= {
30
- :should_receive => [nil],
31
- :with => [:should_receive],
32
- :and_return => [:with, :should_receive],
33
- :and_raise => [:with, :should_receive]
33
+ :with => [nil],
34
+ :and_return => [:with, nil],
35
+ :and_raise => [:with, nil]
34
36
  }
35
37
  end
36
38
  end
37
39
 
38
40
  # @api private
39
41
  class NegativeExpectationChain < ExpectationChain
40
- def initialize(*args, &block)
41
- super(:should_not_receive, *args, &block)
42
- end
43
-
44
42
  # `should_not_receive` causes a failure at the point in time the
45
43
  # message is wrongly received, rather than during `rspec_verify`
46
44
  # at the end of an example. Thus, we should always consider a
@@ -52,12 +50,17 @@ module RSpec
52
50
 
53
51
  private
54
52
 
53
+ def create_message_expectation_on(instance)
54
+ proxy = ::RSpec::Mocks.proxy_for(instance)
55
+ expected_from = IGNORED_BACKTRACE_LINE
56
+ proxy.add_negative_message_expectation(expected_from, *@expectation_args, &@expectation_block)
57
+ end
58
+
55
59
  def invocation_order
56
60
  @invocation_order ||= {
57
- :should_not_receive => [nil],
58
- :with => [:should_receive],
59
- :and_return => [:with, :should_receive],
60
- :and_raise => [:with, :should_receive]
61
+ :with => [nil],
62
+ :and_return => [:with, nil],
63
+ :and_raise => [:with, nil]
61
64
  }
62
65
  end
63
66
  end
@@ -84,6 +84,9 @@ module RSpec
84
84
  if @expectation_set && !message_chains.all_expectations_fulfilled?
85
85
  raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
86
86
  end
87
+ ensure
88
+ stop_all_observation!
89
+ ::RSpec::Mocks.space.remove_any_instance_recorder_for(@klass)
87
90
  end
88
91
 
89
92
  # @private
@@ -103,7 +106,7 @@ module RSpec
103
106
 
104
107
  # @private
105
108
  def playback!(instance, method_name)
106
- RSpec::Mocks::space.add(instance)
109
+ RSpec::Mocks.space.ensure_registered(instance)
107
110
  message_chains.playback!(instance, method_name)
108
111
  @played_methods[method_name] = instance
109
112
  received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
@@ -181,7 +184,7 @@ module RSpec
181
184
  @klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1)
182
185
  def #{method_name}(*args, &blk)
183
186
  klass = ::RSpec::Mocks.method_handle_for(self, :#{method_name}).owner
184
- klass.__recorder.playback!(self, :#{method_name})
187
+ ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, :#{method_name})
185
188
  self.__send__(:#{method_name}, *args, &blk)
186
189
  end
187
190
  EOM
@@ -193,7 +196,7 @@ module RSpec
193
196
  def #{method_name}(*args, &blk)
194
197
  method_name = :#{method_name}
195
198
  klass = ::RSpec::Mocks.method_handle_for(self, :#{method_name}).owner
196
- invoked_instance = klass.__recorder.instance_that_received(method_name)
199
+ invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name)
197
200
  raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by \#{self.inspect} but has already been received by \#{invoked_instance}"
198
201
  end
199
202
  EOM