rspec-mocks 2.99.4 → 3.0.0.beta1

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 (133) hide show
  1. checksums.yaml +14 -6
  2. checksums.yaml.gz.sig +2 -0
  3. data.tar.gz.sig +1 -0
  4. data/Changelog.md +89 -105
  5. data/License.txt +1 -0
  6. data/README.md +77 -57
  7. data/features/argument_matchers/explicit.feature +5 -5
  8. data/features/argument_matchers/general_matchers.feature +10 -10
  9. data/features/argument_matchers/type_matchers.feature +3 -3
  10. data/features/message_expectations/allow_any_instance_of.feature +1 -1
  11. data/features/message_expectations/any_instance.feature +27 -5
  12. data/features/message_expectations/call_original.feature +2 -2
  13. data/features/message_expectations/expect_message_using_expect.feature +2 -2
  14. data/features/message_expectations/expect_message_using_should_receive.feature +2 -2
  15. data/features/message_expectations/receive_counts.feature +7 -7
  16. data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +3 -3
  17. data/features/method_stubs/README.md +3 -0
  18. data/features/method_stubs/any_instance.feature +11 -11
  19. data/features/method_stubs/as_null_object.feature +4 -4
  20. data/features/method_stubs/simple_return_value_with_stub.feature +7 -7
  21. data/features/method_stubs/stub_chain.feature +3 -3
  22. data/features/method_stubs/stub_implementation.feature +2 -2
  23. data/features/method_stubs/to_ary.feature +2 -2
  24. data/features/mutating_constants/hiding_defined_constant.feature +2 -2
  25. data/features/mutating_constants/stub_defined_constant.feature +5 -5
  26. data/features/mutating_constants/stub_undefined_constant.feature +6 -6
  27. data/features/outside_rspec/configuration.feature +0 -2
  28. data/features/outside_rspec/standalone.feature +1 -1
  29. data/features/spies/spy_partial_mock_method.feature +2 -2
  30. data/features/spies/spy_pure_mock_method.feature +5 -5
  31. data/features/spies/spy_unstubbed_method.feature +1 -1
  32. data/features/support/env.rb +10 -1
  33. data/features/test_frameworks/test_unit.feature +1 -1
  34. data/features/verifying_doubles/class_doubles.feature +88 -0
  35. data/features/verifying_doubles/dynamic_classes.feature +72 -0
  36. data/features/verifying_doubles/introduction.feature +85 -0
  37. data/features/verifying_doubles/object_doubles.feature +65 -0
  38. data/features/verifying_doubles/partial_doubles.feature +34 -0
  39. data/lib/rspec/mocks.rb +8 -34
  40. data/lib/rspec/mocks/any_instance/chain.rb +4 -34
  41. data/lib/rspec/mocks/any_instance/expectation_chain.rb +14 -4
  42. data/lib/rspec/mocks/any_instance/message_chains.rb +27 -12
  43. data/lib/rspec/mocks/any_instance/recorder.rb +23 -31
  44. data/lib/rspec/mocks/any_instance/stub_chain.rb +9 -4
  45. data/lib/rspec/mocks/argument_list_matcher.rb +8 -1
  46. data/lib/rspec/mocks/argument_matchers.rb +26 -12
  47. data/lib/rspec/mocks/arity_calculator.rb +66 -0
  48. data/lib/rspec/mocks/configuration.rb +42 -14
  49. data/lib/rspec/mocks/error_generator.rb +34 -10
  50. data/lib/rspec/mocks/example_methods.rb +64 -19
  51. data/lib/rspec/mocks/extensions/marshal.rb +0 -15
  52. data/lib/rspec/mocks/framework.rb +4 -4
  53. data/lib/rspec/mocks/instance_method_stasher.rb +80 -62
  54. data/lib/rspec/mocks/matchers/have_received.rb +18 -14
  55. data/lib/rspec/mocks/matchers/receive.rb +29 -7
  56. data/lib/rspec/mocks/matchers/receive_messages.rb +72 -0
  57. data/lib/rspec/mocks/message_expectation.rb +95 -148
  58. data/lib/rspec/mocks/method_double.rb +77 -139
  59. data/lib/rspec/mocks/method_reference.rb +95 -0
  60. data/lib/rspec/mocks/mock.rb +1 -1
  61. data/lib/rspec/mocks/mutate_const.rb +12 -9
  62. data/lib/rspec/mocks/object_reference.rb +90 -0
  63. data/lib/rspec/mocks/order_group.rb +49 -7
  64. data/lib/rspec/mocks/proxy.rb +72 -33
  65. data/lib/rspec/mocks/proxy_for_nil.rb +2 -2
  66. data/lib/rspec/mocks/space.rb +13 -18
  67. data/lib/rspec/mocks/stub_chain.rb +2 -2
  68. data/lib/rspec/mocks/syntax.rb +61 -36
  69. data/lib/rspec/mocks/targets.rb +40 -19
  70. data/lib/rspec/mocks/test_double.rb +12 -56
  71. data/lib/rspec/mocks/verifying_double.rb +77 -0
  72. data/lib/rspec/mocks/verifying_message_expecation.rb +60 -0
  73. data/lib/rspec/mocks/verifying_proxy.rb +151 -0
  74. data/lib/rspec/mocks/version.rb +1 -1
  75. data/spec/rspec/mocks/and_call_original_spec.rb +34 -30
  76. data/spec/rspec/mocks/and_yield_spec.rb +2 -2
  77. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +1 -1
  78. data/spec/rspec/mocks/any_instance_spec.rb +53 -260
  79. data/spec/rspec/mocks/argument_expectation_spec.rb +4 -4
  80. data/spec/rspec/mocks/arity_calculator_spec.rb +95 -0
  81. data/spec/rspec/mocks/array_including_matcher_spec.rb +41 -0
  82. data/spec/rspec/mocks/at_least_spec.rb +4 -32
  83. data/spec/rspec/mocks/block_return_value_spec.rb +4 -135
  84. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +10 -11
  85. data/spec/rspec/mocks/configuration_spec.rb +79 -0
  86. data/spec/rspec/mocks/double_spec.rb +10 -78
  87. data/spec/rspec/mocks/extensions/marshal_spec.rb +0 -8
  88. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +49 -4
  89. data/spec/rspec/mocks/instance_method_stasher_spec.rb +20 -3
  90. data/spec/rspec/mocks/matchers/have_received_spec.rb +74 -0
  91. data/spec/rspec/mocks/matchers/receive_messages_spec.rb +140 -0
  92. data/spec/rspec/mocks/matchers/receive_spec.rb +82 -42
  93. data/spec/rspec/mocks/methods_spec.rb +1 -1
  94. data/spec/rspec/mocks/{bug_report_830_spec.rb → mock_expectation_error_spec.rb} +4 -3
  95. data/spec/rspec/mocks/mock_ordering_spec.rb +11 -0
  96. data/spec/rspec/mocks/mock_space_spec.rb +10 -1
  97. data/spec/rspec/mocks/mock_spec.rb +26 -82
  98. data/spec/rspec/mocks/multiple_return_value_spec.rb +1 -1
  99. data/spec/rspec/mocks/mutate_const_spec.rb +18 -5
  100. data/spec/rspec/mocks/null_object_mock_spec.rb +6 -4
  101. data/spec/rspec/mocks/options_hash_spec.rb +3 -3
  102. data/spec/rspec/mocks/order_group_spec.rb +27 -0
  103. data/spec/rspec/mocks/partial_mock_spec.rb +101 -1
  104. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +3 -20
  105. data/spec/rspec/mocks/record_messages_spec.rb +4 -4
  106. data/spec/rspec/mocks/serialization_spec.rb +4 -6
  107. data/spec/rspec/mocks/space_spec.rb +3 -3
  108. data/spec/rspec/mocks/stub_chain_spec.rb +0 -12
  109. data/spec/rspec/mocks/stub_spec.rb +23 -44
  110. data/spec/rspec/mocks/test_double_spec.rb +3 -22
  111. data/spec/rspec/mocks/verifying_double_spec.rb +327 -0
  112. data/spec/rspec/mocks/verifying_message_expecation_spec.rb +68 -0
  113. data/spec/rspec/mocks_spec.rb +16 -39
  114. data/spec/spec_helper.rb +29 -18
  115. metadata +131 -86
  116. metadata.gz.sig +1 -0
  117. data/features/message_expectations/expect_any_instance_of.feature +0 -27
  118. data/lib/rspec/mocks/caller_filter.rb +0 -60
  119. data/lib/rspec/mocks/deprecation.rb +0 -26
  120. data/lib/rspec/mocks/extensions/instance_exec.rb +0 -34
  121. data/lib/rspec/mocks/extensions/proc.rb +0 -63
  122. data/lib/spec/mocks.rb +0 -4
  123. data/spec/rspec/mocks/and_return_spec.rb +0 -17
  124. data/spec/rspec/mocks/any_number_of_times_spec.rb +0 -36
  125. data/spec/rspec/mocks/before_all_spec.rb +0 -74
  126. data/spec/rspec/mocks/bug_report_10260_spec.rb +0 -8
  127. data/spec/rspec/mocks/bug_report_10263_spec.rb +0 -27
  128. data/spec/rspec/mocks/bug_report_11545_spec.rb +0 -32
  129. data/spec/rspec/mocks/bug_report_496_spec.rb +0 -17
  130. data/spec/rspec/mocks/bug_report_600_spec.rb +0 -22
  131. data/spec/rspec/mocks/bug_report_7611_spec.rb +0 -16
  132. data/spec/rspec/mocks/bug_report_8165_spec.rb +0 -31
  133. data/spec/rspec/mocks/bug_report_957_spec.rb +0 -22
@@ -6,21 +6,6 @@ module Marshal
6
6
  if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object
7
7
  dump_without_mocks(object, *rest)
8
8
  else
9
- unless ::RSpec::Mocks.configuration.marshal_patched?
10
- RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''))
11
- |Using Marshal.dump on stubbed objects relies on a monkey-patch
12
- |that is being made opt-in in RSpec 3. To silence this warning
13
- |please explicitly enable it:
14
- |
15
- |RSpec.configure do |rspec|
16
- | rspec.mock_with :rspec do |mocks|
17
- | mocks.patch_marshal_to_support_partial_doubles = true
18
- | end
19
- |end
20
- |
21
- |Called from #{RSpec::CallerFilter.first_non_rspec_line}."
22
- EOS
23
- end
24
9
  dump_without_mocks(object.dup, *rest)
25
10
  end
26
11
  end
@@ -2,10 +2,8 @@
2
2
  # supports wrapping rspec's mocking functionality without invading every
3
3
  # object in the system.
4
4
 
5
- require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter)
6
- require 'rspec/mocks/deprecation'
7
- require 'rspec/mocks/extensions/instance_exec'
8
- require 'rspec/mocks/extensions/proc'
5
+ require 'rspec/support/caller_filter'
6
+ require 'rspec/support/warnings'
9
7
  require 'rspec/mocks/instance_method_stasher'
10
8
  require 'rspec/mocks/method_double'
11
9
  require 'rspec/mocks/argument_matchers'
@@ -30,7 +28,9 @@ require 'rspec/mocks/any_instance/recorder'
30
28
  require 'rspec/mocks/mutate_const'
31
29
  require 'rspec/mocks/matchers/have_received'
32
30
  require 'rspec/mocks/matchers/receive'
31
+ require 'rspec/mocks/matchers/receive_messages'
33
32
  require 'rspec/mocks/stub_chain'
34
33
  require 'rspec/mocks/targets'
35
34
  require 'rspec/mocks/syntax'
36
35
  require 'rspec/mocks/configuration'
36
+ require 'rspec/mocks/verifying_double'
@@ -2,91 +2,109 @@ module RSpec
2
2
  module Mocks
3
3
  # @private
4
4
  class InstanceMethodStasher
5
- def initialize(klass, method)
6
- @klass = klass
5
+ def initialize(object, method)
6
+ @object = object
7
7
  @method = method
8
+ @klass = (class << object; self; end)
8
9
 
10
+ @original_method = nil
9
11
  @method_is_stashed = false
10
12
  end
11
13
 
12
- # @private
13
- def method_is_stashed?
14
- @method_is_stashed
15
- end
14
+ attr_reader :original_method
16
15
 
17
- # @private
18
- def stash
19
- return if !method_defined_directly_on_klass? || @method_is_stashed
20
-
21
- @klass.__send__(:alias_method, stashed_method_name, @method)
22
- @method_is_stashed = true
23
- end
16
+ if RUBY_VERSION.to_f < 1.9
17
+ # @private
18
+ def method_is_stashed?
19
+ @method_is_stashed
20
+ end
24
21
 
25
- private
22
+ # @private
23
+ def stash
24
+ return if !method_defined_directly_on_klass? || @method_is_stashed
26
25
 
27
- # @private
28
- def method_defined_directly_on_klass?
29
- method_defined_on_klass? && method_owned_by_klass?
30
- end
26
+ @klass.__send__(:alias_method, stashed_method_name, @method)
27
+ @method_is_stashed = true
28
+ end
31
29
 
32
- # @private
33
- def method_defined_on_klass?(klass = @klass)
34
- klass.method_defined?(@method) || klass.private_method_defined?(@method)
35
- end
30
+ # @private
31
+ def stashed_method_name
32
+ "obfuscated_by_rspec_mocks__#{@method}"
33
+ end
34
+ private :stashed_method_name
36
35
 
37
- if ::UnboundMethod.method_defined?(:owner)
38
36
  # @private
39
- def method_owned_by_klass?
40
- owner = @klass.instance_method(@method).owner
41
-
42
- # On Ruby 2.0.0+ the owner of a method on a class which has been
43
- # `prepend`ed may actually be an instance, e.g.
44
- # `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
45
- owner = owner.class unless owner.is_a? Class
46
-
47
- # On 1.8 (and some 1.9s -- e.g. rubinius) aliased methods
48
- # can report the wrong owner. Example:
49
- # class MyClass
50
- # class << self
51
- # alias alternate_new new
52
- # end
53
- # end
54
- #
55
- # MyClass.owner(:alternate_new) returns `Class` on 1.8,
56
- # but we need to consider the owner to be `MyClass` because
57
- # it is not actually available on `Class` but is on `MyClass`.
58
- # Hence, we verify that the owner actually has the method defined.
59
- # If the given owner does not have the method defined, we assume
60
- # that the method is actually owned by @klass.
61
- owner == @klass || !(method_defined_on_klass?(owner))
37
+ def restore
38
+ return unless @method_is_stashed
39
+
40
+ if @klass.__send__(:method_defined?, @method)
41
+ @klass.__send__(:undef_method, @method)
42
+ end
43
+ @klass.__send__(:alias_method, @method, stashed_method_name)
44
+ @klass.__send__(:remove_method, stashed_method_name)
45
+ @method_is_stashed = false
62
46
  end
63
47
  else
48
+
49
+ # @private
50
+ def method_is_stashed?
51
+ !!@original_method
52
+ end
53
+
64
54
  # @private
65
- def method_owned_by_klass?
66
- # On 1.8.6, which does not support Method#owner, we have no choice but
67
- # to assume it's defined on the klass even if it may be defined on
68
- # a superclass.
69
- true
55
+ def stash
56
+ return if !method_defined_directly_on_klass?
57
+ @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method)
58
+ end
59
+
60
+ # @private
61
+ def restore
62
+ return unless @original_method
63
+
64
+ if @klass.__send__(:method_defined?, @method)
65
+ @klass.__send__(:undef_method, @method)
66
+ end
67
+
68
+ @klass.__send__(:define_method, @method, @original_method)
69
+ @original_method = nil
70
70
  end
71
71
  end
72
72
 
73
- public
73
+ private
74
74
 
75
75
  # @private
76
- def stashed_method_name
77
- "obfuscated_by_rspec_mocks__#{@method}"
76
+ def method_defined_directly_on_klass?
77
+ method_defined_on_klass? && method_owned_by_klass?
78
78
  end
79
79
 
80
80
  # @private
81
- def restore
82
- return unless @method_is_stashed
81
+ def method_defined_on_klass?(klass = @klass)
82
+ klass.method_defined?(@method) || klass.private_method_defined?(@method)
83
+ end
83
84
 
84
- if @klass.__send__(:method_defined?, @method)
85
- @klass.__send__(:undef_method, @method)
86
- end
87
- @klass.__send__(:alias_method, @method, stashed_method_name)
88
- @klass.__send__(:remove_method, stashed_method_name)
89
- @method_is_stashed = false
85
+ def method_owned_by_klass?
86
+ owner = @klass.instance_method(@method).owner
87
+
88
+ # On Ruby 2.0.0+ the owner of a method on a class which has been
89
+ # `prepend`ed may actually be an instance, e.g.
90
+ # `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
91
+ owner = owner.class unless Module === owner
92
+
93
+ # On some 1.9s (e.g. rubinius) aliased methods
94
+ # can report the wrong owner. Example:
95
+ # class MyClass
96
+ # class << self
97
+ # alias alternate_new new
98
+ # end
99
+ # end
100
+ #
101
+ # MyClass.owner(:alternate_new) returns `Class` when incorrect,
102
+ # but we need to consider the owner to be `MyClass` because
103
+ # it is not actually available on `Class` but is on `MyClass`.
104
+ # Hence, we verify that the owner actually has the method defined.
105
+ # If the given owner does not have the method defined, we assume
106
+ # that the method is actually owned by @klass.
107
+ owner == @klass || !(method_defined_on_klass?(owner))
90
108
  end
91
109
  end
92
110
  end
@@ -4,25 +4,31 @@ module RSpec
4
4
  class HaveReceived
5
5
  COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice)
6
6
  ARGS_CONSTRAINTS = %w(with)
7
- CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS
7
+ CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w(ordered)
8
8
 
9
- def initialize(method_name)
9
+ def initialize(method_name, &block)
10
10
  @method_name = method_name
11
+ @block = block
11
12
  @constraints = []
12
13
  @subject = nil
13
14
  end
14
15
 
15
- def matches?(subject)
16
+ def name
17
+ "have_received"
18
+ end
19
+
20
+ def matches?(subject, &block)
21
+ @block ||= block
16
22
  @subject = subject
17
23
  @expectation = expect
18
- expected_messages_received?
24
+ expected_messages_received_in_order?
19
25
  end
20
26
 
21
27
  def does_not_match?(subject)
22
28
  @subject = subject
23
29
  ensure_count_unconstrained
24
30
  @expectation = expect.never
25
- expected_messages_received?
31
+ expected_messages_received_in_order?
26
32
  end
27
33
 
28
34
  def failure_message
@@ -44,14 +50,12 @@ module RSpec
44
50
  end
45
51
  end
46
52
 
47
- private
53
+ private
48
54
 
49
55
  def expect
50
- @expect ||= begin
51
- expectation = mock_proxy.build_expectation(@method_name)
52
- apply_constraints_to expectation
53
- expectation
54
- end
56
+ expectation = mock_proxy.build_expectation(@method_name)
57
+ apply_constraints_to expectation
58
+ expectation
55
59
  end
56
60
 
57
61
  def apply_constraints_to(expectation)
@@ -80,9 +84,9 @@ module RSpec
80
84
  error.message
81
85
  end
82
86
 
83
- def expected_messages_received?
84
- mock_proxy.replay_received_message_on @expectation
85
- @expectation.expected_messages_received?
87
+ def expected_messages_received_in_order?
88
+ mock_proxy.replay_received_message_on @expectation, &@block
89
+ @expectation.expected_messages_received? && @expectation.ensure_expected_ordering_received!
86
90
  end
87
91
 
88
92
  def mock_proxy
@@ -6,10 +6,20 @@ module RSpec
6
6
  @message = message
7
7
  @block = block
8
8
  @recorded_customizations = []
9
- @backtrace_line = CallerFilter.first_non_rspec_line
9
+
10
+ # MRI, JRuby and RBX report the caller inconsistently; MRI
11
+ # reports an extra "in `new'" line in the backtrace that the
12
+ # others do not include. The safest way to find the right
13
+ # line is to search for the first line BEFORE rspec/mocks/syntax.rb.
14
+ @backtrace_line = CallerFilter.first_non_rspec_line
15
+ end
16
+
17
+ def name
18
+ "receive"
10
19
  end
11
20
 
12
21
  def setup_expectation(subject, &block)
22
+ warn_if_any_instance("expect", subject)
13
23
  setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
14
24
  end
15
25
  alias matches? setup_expectation
@@ -19,11 +29,14 @@ module RSpec
19
29
  # where `and_return` is meant to raise an error
20
30
  @recorded_customizations.unshift Customization.new(:never, [], nil)
21
31
 
32
+ warn_if_any_instance("expect", subject)
33
+
22
34
  setup_expectation(subject, &block)
23
35
  end
24
36
  alias does_not_match? setup_negative_expectation
25
37
 
26
38
  def setup_allowance(subject, &block)
39
+ warn_if_any_instance("allow", subject)
27
40
  setup_mock_proxy_method_substitute(subject, :add_stub, block)
28
41
  end
29
42
 
@@ -42,16 +55,25 @@ module RSpec
42
55
  MessageExpectation.public_instance_methods(false).each do |method|
43
56
  next if method_defined?(method)
44
57
 
45
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
46
- def #{method}(*args, &block)
47
- @recorded_customizations << Customization.new(#{method.inspect}, args, block)
48
- self
49
- end
50
- RUBY
58
+ define_method(method) do |*args, &block|
59
+ @recorded_customizations << Customization.new(method, args, block)
60
+ self
61
+ end
51
62
  end
52
63
 
53
64
  private
54
65
 
66
+ def warn_if_any_instance(expression, subject)
67
+ if AnyInstance::Recorder === subject
68
+ RSpec.warning(
69
+ "`#{expression}(#{subject.klass}.any_instance).to` " <<
70
+ "is probably not what you meant, it does not operate on " <<
71
+ "any instance of `#{subject.klass}`. " <<
72
+ "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead."
73
+ )
74
+ end
75
+ end
76
+
55
77
  def setup_mock_proxy_method_substitute(subject, method, block)
56
78
  proxy = ::RSpec::Mocks.proxy_for(subject)
57
79
  setup_method_substitute(proxy, method, block, @backtrace_line)
@@ -0,0 +1,72 @@
1
+ module RSpec
2
+ module Mocks
3
+ module Matchers
4
+ class ReceiveMessages
5
+
6
+ def initialize(message_return_value_hash)
7
+ @message_return_value_hash = message_return_value_hash
8
+ @backtrace_line = CallerFilter.first_non_rspec_line
9
+ end
10
+
11
+ def name
12
+ "receive_messages"
13
+ end
14
+
15
+ def setup_expectation(subject)
16
+ warn_about_block if block_given?
17
+ each_message_on( proxy_on(subject) ) do |host, message, return_value|
18
+ host.add_simple_expectation(message, return_value, @backtrace_line)
19
+ end
20
+ end
21
+ alias matches? setup_expectation
22
+
23
+ def setup_negative_expectation(subject)
24
+ raise NegationUnsupportedError,
25
+ "`expect(...).to_not receive_messages` is not supported since it " +
26
+ "doesn't really make sense. What would it even mean?"
27
+ end
28
+ alias does_not_match? setup_negative_expectation
29
+
30
+ def setup_allowance(subject)
31
+ warn_about_block if block_given?
32
+ each_message_on( proxy_on(subject) ) do |host, message, return_value|
33
+ host.add_simple_stub(message, return_value)
34
+ end
35
+ end
36
+
37
+ def setup_any_instance_expectation(subject)
38
+ warn_about_block if block_given?
39
+ each_message_on( any_instance_of(subject) ) do |host, message, return_value|
40
+ host.should_receive(message).and_return(return_value)
41
+ end
42
+ end
43
+
44
+ def setup_any_instance_allowance(subject)
45
+ warn_about_block if block_given?
46
+ any_instance_of(subject).stub(@message_return_value_hash)
47
+ end
48
+
49
+ def warn_about_block
50
+ raise "Implementation blocks aren't supported with `receive_messages`"
51
+ end
52
+
53
+ private
54
+
55
+ def proxy_on(subject)
56
+ ::RSpec::Mocks.proxy_for(subject)
57
+ end
58
+
59
+ def any_instance_of(subject)
60
+ ::RSpec::Mocks.any_instance_recorder_for(subject)
61
+ end
62
+
63
+ def each_message_on(host)
64
+ @message_return_value_hash.each do |message, value|
65
+ yield host, message, value
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,10 +1,42 @@
1
1
  module RSpec
2
2
  module Mocks
3
3
 
4
+ # A message expectation that only allows concrete return values to be set
5
+ # for a message. While this same effect can be achieved using a standard
6
+ # MessageExpecation, this version is much faster and so can be used as an
7
+ # optimization.
8
+ class SimpleMessageExpectation
9
+
10
+ def initialize(message, response, error_generator, backtrace_line = nil)
11
+ @message, @response, @error_generator, @backtrace_line = message, response, error_generator, backtrace_line
12
+ @received = false
13
+ end
14
+
15
+ def invoke(*_)
16
+ @received = true
17
+ @response
18
+ end
19
+
20
+ def matches?(message, *_)
21
+ @message == message
22
+ end
23
+
24
+ def called_max_times?
25
+ false
26
+ end
27
+
28
+ def verify_messages_received
29
+ InsertOntoBacktrace.line(@backtrace_line) do
30
+ unless @received
31
+ @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
4
37
  class MessageExpectation
5
38
  # @private
6
39
  attr_accessor :error_generator, :implementation
7
- attr_accessor :warn_about_yielding_receiver_to_implementation_block
8
40
  attr_reader :message
9
41
  attr_reader :orig_object
10
42
  attr_writer :expected_received_count, :expected_from, :argument_list_matcher
@@ -17,14 +49,14 @@ module RSpec
17
49
  @error_generator.opts = opts
18
50
  @expected_from = expected_from
19
51
  @method_double = method_double
20
- @have_warned_about_yielding_receiver = false
21
52
  @orig_object = @method_double.object
22
- @warn_about_yielding_receiver_to_implementation_block = false
23
53
  @message = @method_double.method_name
24
54
  @actual_received_count = 0
25
55
  @expected_received_count = expected_received_count
26
- @argument_list_matcher = ArgumentListMatcher.new(ArgumentMatchers::AnyArgsMatcher.new)
56
+ @argument_list_matcher = ArgumentListMatcher::MATCH_ALL
27
57
  @order_group = expectation_ordering
58
+ @order_group.register(self)
59
+ @ordered = false
28
60
  @at_least = @at_most = @exactly = nil
29
61
  @args_to_yield = []
30
62
  @failed_fast = nil
@@ -35,6 +67,8 @@ module RSpec
35
67
  self.inner_implementation_action = implementation_block
36
68
  end
37
69
 
70
+ # @private
71
+
38
72
  # @private
39
73
  def expected_args
40
74
  @argument_list_matcher.expected_args
@@ -52,8 +86,8 @@ module RSpec
52
86
  # If the message is received more times than there are values, the last
53
87
  # value is received for every subsequent call.
54
88
  #
55
- # The block format is deprecated in favor of just passing a block to the
56
- # stub method.
89
+ # The block format is still supported, but is unofficially deprecated in
90
+ # favor of just passing a block to the stub method.
57
91
  #
58
92
  # @example
59
93
  #
@@ -69,35 +103,28 @@ module RSpec
69
103
  # counter.count # => 3
70
104
  # # etc
71
105
  #
72
- # # Deprecated ...
106
+ # # Supported, but ...
73
107
  # counter.stub(:count).and_return { 1 }
74
108
  # counter.count # => 1
75
109
  #
76
- # # ... use this instead
110
+ # # ... this is prefered
77
111
  # counter.stub(:count) { 1 }
78
112
  # counter.count # => 1
79
113
  def and_return(*values, &implementation)
80
114
  if negative?
81
- RSpec.deprecate "`and_return` on a negative message expectation"
82
- end
83
-
84
- @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
85
-
86
- if implementation
87
- RSpec.deprecate('`and_return { value }`',
88
- :replacement => '`and_return(value)` or an implementation block without `and_return`')
89
- self.inner_implementation_action = implementation
115
+ raise "`and_return` is not supported with negative message expectations"
90
116
  else
91
- if values.empty?
92
- RSpec.warn_deprecation('`and_return` without arguments is deprecated. ' +
93
- 'Remove the `and_return`. ' +
94
- "Called from #{CallerFilter.first_non_rspec_line}.")
117
+ @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
118
+
119
+ if implementation
120
+ # TODO: deprecate `and_return { value }`
121
+ self.inner_implementation_action = implementation
122
+ else
123
+ self.terminal_implementation_action = AndReturnImplementation.new(values)
95
124
  end
96
125
 
97
- self.terminal_implementation_action = AndReturnImplementation.new(values)
126
+ nil
98
127
  end
99
-
100
- nil
101
128
  end
102
129
 
103
130
  def and_yield_receiver_to_implementation
@@ -121,9 +148,12 @@ module RSpec
121
148
  # counter.increment
122
149
  # expect(counter.count).to eq(original_count + 1)
123
150
  def and_call_original
124
- if @method_double.object.is_a?(RSpec::Mocks::TestDouble)
151
+ if RSpec::Mocks::TestDouble === @method_double.object
125
152
  @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
126
153
  else
154
+ if implementation.inner_action
155
+ RSpec.warning("You're overriding a previous implementation for this stub")
156
+ end
127
157
  @implementation = AndCallOriginalImplementation.new(@method_double.original_method)
128
158
  @yield_receiver_to_implementation_block = false
129
159
  end
@@ -181,7 +211,7 @@ module RSpec
181
211
  #
182
212
  # stream.stub(:open).and_yield(StringIO.new)
183
213
  def and_yield(*args, &block)
184
- yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block
214
+ yield @eval_context = Object.new if block
185
215
  @args_to_yield << args
186
216
  self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
187
217
  self
@@ -239,39 +269,21 @@ module RSpec
239
269
 
240
270
  # @private
241
271
  def verify_messages_received
242
- generate_error unless expected_messages_received? || failed_fast?
243
- rescue RSpec::Mocks::MockExpectationError => error
244
- error.backtrace.insert(0, @expected_from)
245
- Kernel::raise error
246
- end
247
-
248
- # @private
249
- def display_any_instance_deprecation_warning_if_necessary(block)
250
- if passing_an_additional_arg_would_break_block?(block) &&
251
- should_display_any_instance_deprecation_warning
252
- line = if block.respond_to?(:source_location)
253
- block.source_location.join(':')
254
- else
255
- @any_instance_source_line
256
- end
257
-
258
- display_any_instance_deprecation_warning(line)
259
- @have_warned_about_yielding_receiver = true
272
+ InsertOntoBacktrace.line(@expected_from) do
273
+ generate_error unless expected_messages_received? || failed_fast?
260
274
  end
261
275
  end
262
276
 
263
- # @private
264
- def passing_an_additional_arg_would_break_block?(block)
265
- return false unless block
266
- return true if block.lambda?
267
- !block.arity.zero?
268
- end
269
-
270
277
  # @private
271
278
  def expected_messages_received?
272
279
  ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
273
280
  end
274
281
 
282
+ def ensure_expected_ordering_received!
283
+ @order_group.verify_invocation_order(self) if @ordered
284
+ true
285
+ end
286
+
275
287
  # @private
276
288
  def ignoring_args?
277
289
  @expected_received_count == :any
@@ -351,18 +363,13 @@ module RSpec
351
363
  # cart.add(Book.new(:isbn => 1934356379))
352
364
  # # => passes
353
365
  def with(*args, &block)
354
- if block_given?
355
- if args.empty?
356
- RSpec.deprecate "Using the return value of a `with` block to validate passed arguments rather than as an implementation",
357
- :replacement => "the `satisfy` matcher, a custom matcher or validate the arguments in an implementation block"
358
- else
359
- self.inner_implementation_action = block
360
- end
361
- elsif args.empty?
362
- RSpec.deprecate "Using `with` without arguments", :replacement => "`with(no_args)`"
366
+ if args.empty?
367
+ raise ArgumentError,
368
+ "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
363
369
  end
364
370
 
365
- @argument_list_matcher = ArgumentListMatcher.new(*args, &block)
371
+ self.inner_implementation_action = block
372
+ @argument_list_matcher = ArgumentListMatcher.new(*args)
366
373
  self
367
374
  end
368
375
 
@@ -385,12 +392,14 @@ module RSpec
385
392
  #
386
393
  # dealer.should_receive(:deal_card).at_least(9).times
387
394
  def at_least(n, &block)
395
+ set_expected_received_count :at_least, n
396
+
388
397
  if n == 0
389
- RSpec.deprecate "at_least(0) with should_receive", :replacement => "stub"
398
+ raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
390
399
  end
391
400
 
392
401
  self.inner_implementation_action = block
393
- set_expected_received_count :at_least, n
402
+
394
403
  self
395
404
  end
396
405
 
@@ -418,15 +427,6 @@ module RSpec
418
427
  self
419
428
  end
420
429
 
421
-
422
- # Allows an expected message to be received any number of times.
423
- def any_number_of_times(&block)
424
- RSpec.deprecate "any_number_of_times", :replacement => "stub"
425
- self.inner_implementation_action = block
426
- @expected_received_count = :any
427
- self
428
- end
429
-
430
430
  # Expect a message not to be received at all.
431
431
  #
432
432
  # @example
@@ -469,11 +469,15 @@ module RSpec
469
469
  # api.should_receive(:finish).ordered
470
470
  def ordered(&block)
471
471
  self.inner_implementation_action = block
472
- @order_group.register(self)
473
472
  @ordered = true
474
473
  self
475
474
  end
476
475
 
476
+ # @private
477
+ def ordered?
478
+ @ordered
479
+ end
480
+
477
481
  # @private
478
482
  def negative_expectation_for?(message)
479
483
  @message == message && negative?
@@ -489,38 +493,6 @@ module RSpec
489
493
  @actual_received_count += 1
490
494
  end
491
495
 
492
- def warn_about_receiver_passing(any_instance_source_line)
493
- @any_instance_source_line = any_instance_source_line
494
- @warn_about_yielding_receiver_to_implementation_block = true
495
- end
496
-
497
- def should_display_any_instance_deprecation_warning
498
- warn_about_yielding_receiver_to_implementation_block &&
499
- !@have_warned_about_yielding_receiver
500
- end
501
-
502
- def display_any_instance_deprecation_warning(block_source_line)
503
- RSpec.warn_deprecation(<<MSG
504
- In RSpec 3, `any_instance` implementation blocks will be yielded the receiving
505
- instance as the first block argument to allow the implementation block to use
506
- the state of the receiver. To maintain compatibility with RSpec 3 you need to
507
- either set rspec-mocks' `yield_receiver_to_any_instance_implementation_blocks`
508
- config option to `false` OR set it to `true` and update your `any_instance`
509
- implementation blocks to account for the first block argument being the receiving instance.
510
-
511
- To set the config option, use a snippet like:
512
-
513
- RSpec.configure do |rspec|
514
- rspec.mock_with :rspec do |mocks|
515
- mocks.yield_receiver_to_any_instance_implementation_blocks = false
516
- end
517
- end
518
-
519
- Your `any_instance` implementation block is declared at: #{block_source_line}
520
- MSG
521
- )
522
- end
523
-
524
496
  private
525
497
 
526
498
  def failed_fast?
@@ -543,7 +515,7 @@ MSG
543
515
  end
544
516
 
545
517
  def inner_implementation_action=(action)
546
- display_any_instance_deprecation_warning_if_necessary(action)
518
+ RSpec.warning("You're overriding a previous implementation for this stub") if implementation.inner_action
547
519
  implementation.inner_action = action if action
548
520
  end
549
521
 
@@ -561,10 +533,6 @@ MSG
561
533
  @error_generator = error_generator
562
534
  end
563
535
 
564
- def arity
565
- -1
566
- end
567
-
568
536
  def call(*args_to_ignore, &block)
569
537
  return if @args_to_yield.empty? && @eval_context.nil?
570
538
 
@@ -587,10 +555,6 @@ MSG
587
555
  @values_to_return = values_to_return
588
556
  end
589
557
 
590
- def arity
591
- -1
592
- end
593
-
594
558
  def call(*args_to_ignore, &block)
595
559
  if @values_to_return.size > 1
596
560
  @values_to_return.shift
@@ -608,39 +572,10 @@ MSG
608
572
 
609
573
  def call(*args, &block)
610
574
  actions.map do |action|
611
- if action.respond_to?(:lambda?) && action.lambda? && action.arity != args.size
612
- RSpec.deprecate "stubbing implementations with mismatched arity",
613
- :call_site => CallerFilter.first_non_rspec_line
614
- end
615
- action.call(*arg_slice_for(args, action.arity), &block)
575
+ action.call(*args, &block)
616
576
  end.last
617
577
  end
618
578
 
619
- if RUBY_VERSION.to_f > 1.8
620
- def arg_slice_for(args, arity)
621
- if arity >= 0
622
- args.slice(0, arity)
623
- else
624
- args
625
- end
626
- end
627
- else
628
- # 1.8.7's `arity` lies somtimes:
629
- # Given:
630
- # def print_arity(&b) puts b.arity; end
631
- #
632
- # This prints 1:
633
- # print_arity { |a, b, c, &bl| }
634
- #
635
- # But this prints 3:
636
- # print_arity { |a, b, c| }
637
- #
638
- # Given that it lies, we can't trust it and we don't slice the args.
639
- def arg_slice_for(args, arity)
640
- args
641
- end
642
- end
643
-
644
579
  def present?
645
580
  actions.any?
646
581
  end
@@ -661,10 +596,6 @@ MSG
661
596
 
662
597
  CannotModifyFurtherError = Class.new(StandardError)
663
598
 
664
- def arity
665
- @method.arity
666
- end
667
-
668
599
  def initial_action=(value)
669
600
  raise cannot_modify_further_error
670
601
  end
@@ -681,6 +612,10 @@ MSG
681
612
  true
682
613
  end
683
614
 
615
+ def inner_action
616
+ true
617
+ end
618
+
684
619
  def call(*args, &block)
685
620
  @method.call(*args, &block)
686
621
  end
@@ -692,5 +627,17 @@ MSG
692
627
  "to call the original implementation, and cannot be modified further."
693
628
  end
694
629
  end
630
+
631
+ # Insert original locations into stacktraces
632
+ # @api private
633
+ class InsertOntoBacktrace
634
+ def self.line(location)
635
+ yield
636
+ rescue RSpec::Mocks::MockExpectationError => error
637
+ error.backtrace.insert(0, location)
638
+ Kernel::raise error
639
+ end
640
+ end
641
+
695
642
  end
696
643
  end