rspec-mocks 2.99.4 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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