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
@@ -0,0 +1,65 @@
1
+ Feature: Using an object double
2
+
3
+ `object_double` can be used to create a double from an existing "template"
4
+ object, from which it verifies that any stubbed methods on the double also
5
+ exist on the template. This is useful for objects that are readily
6
+ constructable, but may have far-reaching side-effects such as talking to a
7
+ database or external API. In this case, using a double rather than the real
8
+ thing allows you to focus on the communication patterns of the object's
9
+ interface without having to worry about accidentally causing side-effects.
10
+ Object doubles can also be used to verify methods defined on an object using
11
+ `method_missing`, which is not possible with `instance_double`.
12
+
13
+ In addition, `object_double` can be used with specific constant values, as
14
+ shown below. This is for niche situations, such as when dealing with
15
+ singleton objects.
16
+
17
+ Scenario: doubling an existing object
18
+ Given a file named "spec/user_spec.rb" with:
19
+ """ruby
20
+ class User
21
+ # Don't want to accidentally trigger this!
22
+ def save; sleep 100; end
23
+ end
24
+
25
+ def save_user(user)
26
+ "saved!" if user.save
27
+ end
28
+
29
+ describe '#save_user' do
30
+ it 'renders message on success' do
31
+ user = object_double(User.new, :save => true)
32
+ expect(save_user(user)).to eq("saved!")
33
+ end
34
+ end
35
+ """
36
+ When I run `rspec spec/user_spec.rb`
37
+ Then the examples should all pass
38
+
39
+
40
+ Scenario: doubling a constant object
41
+ Given a file named "spec/email_spec.rb" with:
42
+ """ruby
43
+ require 'logger'
44
+
45
+ module MyApp
46
+ LOGGER = Logger.new("myapp")
47
+ end
48
+
49
+ class Email
50
+ def self.send_to(recipient)
51
+ MyApp::LOGGER.info("Sent to #{recipient}")
52
+ # other emailing logic
53
+ end
54
+ end
55
+
56
+ describe Email do
57
+ it 'logs a message when sending' do
58
+ logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const
59
+ Email.send_to('hello@foo.com')
60
+ expect(logger).to have_received(:info).with("Sent to hello@foo.com")
61
+ end
62
+ end
63
+ """
64
+ When I run `rspec spec/email_spec.rb`
65
+ Then the examples should all pass
@@ -0,0 +1,34 @@
1
+ Feature: Partial doubles
2
+
3
+ When the `verify_partial_doubles` configuration option is set, the same arity
4
+ and method existince checks that are performed for `object_double` are also
5
+ performed on partial doubles. You should set this unless you have a good
6
+ reason not to. It defaults to off only for backwards compatibility.
7
+
8
+ Scenario: doubling an existing object
9
+ Given a file named "spec/user_spec.rb" with:
10
+ """ruby
11
+ class User
12
+ def save; false; end
13
+ end
14
+
15
+ def save_user(user)
16
+ "saved!" if user.save
17
+ end
18
+
19
+ RSpec.configure do |config|
20
+ config.mock_with :rspec do |mocks|
21
+ mocks.verify_partial_doubles = true
22
+ end
23
+ end
24
+
25
+ describe '#save_user' do
26
+ it 'renders message on success' do
27
+ user = User.new
28
+ expect(user).to receive(:saave).and_return(true) # Typo in name
29
+ expect(save_user(user)).to eq("saved!")
30
+ end
31
+ end
32
+ """
33
+ When I run `rspec spec/user_spec.rb`
34
+ Then the output should contain "1 example, 1 failure"
@@ -2,25 +2,16 @@ require 'rspec/mocks/framework'
2
2
  require 'rspec/mocks/version'
3
3
 
4
4
  module RSpec
5
+
5
6
  module Mocks
6
7
  class << self
7
8
  attr_accessor :space
8
9
 
9
- def setup(host=nil)
10
- host_is_from_rspec_core = defined?(::RSpec::Core::ExampleGroup) && host.is_a?(::RSpec::Core::ExampleGroup)
11
- if host
12
- unless host_is_from_rspec_core
13
- RSpec.deprecate(
14
- "The host argument to `RSpec::Mocks.setup`",
15
- :replacement => "`include RSpec::Mocks::ExampleMethods` in #{host.inspect}"
16
- )
17
- end
18
-
19
- (class << host; self; end).class_eval do
20
- include RSpec::Mocks::ExampleMethods
21
- end
10
+ def setup(host)
11
+ (class << host; self; end).class_exec do
12
+ include RSpec::Mocks::ExampleMethods
22
13
  end
23
- space.outside_example = false
14
+ self.space ||= RSpec::Mocks::Space.new
24
15
  end
25
16
 
26
17
  def verify
@@ -29,7 +20,6 @@ module RSpec
29
20
 
30
21
  def teardown
31
22
  space.reset_all
32
- space.outside_example = true
33
23
  end
34
24
 
35
25
  def proxy_for(object)
@@ -61,7 +51,7 @@ module RSpec
61
51
  CallerFilter.first_non_rspec_line
62
52
  }
63
53
  ::RSpec::Mocks.proxy_for(subject).
64
- add_stub(orig_caller, message.to_sym, opts, &block)
54
+ add_stub(orig_caller, message, opts, &block)
65
55
  end
66
56
 
67
57
  # Sets a message expectation on `subject`.
@@ -80,7 +70,7 @@ module RSpec
80
70
  CallerFilter.first_non_rspec_line
81
71
  }
82
72
  ::RSpec::Mocks.proxy_for(subject).
83
- add_message_expectation(orig_caller, message.to_sym, opts, &block)
73
+ add_message_expectation(orig_caller, message, opts, &block)
84
74
  end
85
75
 
86
76
  # @api private
@@ -105,22 +95,6 @@ module RSpec
105
95
 
106
96
  # @private
107
97
  IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
108
-
109
- self.space = RSpec::Mocks::Space.new
110
-
111
- DEPRECATED_CONSTANTS =
112
- {
113
- :Mock => Double,
114
- :ConstantStubber => ConstantMutator,
115
- }
116
-
117
- def self.const_missing(name)
118
- if const = DEPRECATED_CONSTANTS[name]
119
- RSpec.deprecate("RSpec::Mocks::#{name}", :replacement => const.name)
120
- const
121
- else
122
- super
123
- end
124
- end
125
98
  end
126
99
  end
100
+
@@ -6,8 +6,6 @@ module RSpec
6
6
  @recorder = recorder
7
7
  @expectation_args = args
8
8
  @expectation_block = block
9
- @source_line = CallerFilter.first_non_rspec_line
10
- ensure_expectation_block_has_source_location
11
9
  end
12
10
 
13
11
  module Customizations
@@ -19,11 +17,9 @@ module RSpec
19
17
  # @see RSpec::Mocks::MessageExpectation#$1
20
18
  #
21
19
  def self.record(method_name)
22
- class_eval(<<-EOM, __FILE__, __LINE__ + 1)
23
- def #{method_name}(*args, &block)
24
- record(:#{method_name}, *args, &block)
25
- end
26
- EOM
20
+ define_method(method_name) do |*args, &block|
21
+ record(method_name, *args, &block)
22
+ end
27
23
  end
28
24
 
29
25
  record :and_return
@@ -73,21 +69,8 @@ module RSpec
73
69
 
74
70
  private
75
71
 
76
- def create_message_expectation_on(instance)
77
- me = yield(::RSpec::Mocks.proxy_for(instance), IGNORED_BACKTRACE_LINE)
78
-
79
- if RSpec::Mocks.configuration.should_warn_about_any_instance_blocks?
80
- me.warn_about_receiver_passing(@source_line)
81
- me.display_any_instance_deprecation_warning_if_necessary(@expectation_block)
82
- elsif RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
83
- me.and_yield_receiver_to_implementation
84
- end
85
-
86
- me
87
- end
88
-
89
72
  def negated?
90
- messages.any? { |(message, *_), _| message.to_sym == :never }
73
+ messages.any? { |(message, *_), _| message == :never }
91
74
  end
92
75
 
93
76
  def messages
@@ -103,19 +86,6 @@ module RSpec
103
86
  messages << [args.unshift(rspec_method_name), block]
104
87
  self
105
88
  end
106
-
107
- if Proc.method_defined?(:source_location)
108
- def ensure_expectation_block_has_source_location; end
109
- else
110
- def ensure_expectation_block_has_source_location
111
- return unless @expectation_block
112
- source_location = CallerFilter.first_non_rspec_line.split(':')
113
-
114
- @expectation_block.extend Module.new {
115
- define_method(:source_location) { source_location }
116
- }
117
- end
118
- end
119
89
  end
120
90
  end
121
91
  end
@@ -12,15 +12,25 @@ module RSpec
12
12
  super
13
13
  end
14
14
 
15
- private
16
-
15
+ private
17
16
  def verify_invocation_order(rspec_method_name, *args, &block)
18
17
  end
18
+ end
19
+
20
+ # @api private
21
+ class PositiveExpectationChain < ExpectationChain
22
+
23
+ private
19
24
 
20
25
  def create_message_expectation_on(instance)
21
- super do |proxy, expected_from|
22
- proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
26
+ proxy = ::RSpec::Mocks.proxy_for(instance)
27
+ expected_from = IGNORED_BACKTRACE_LINE
28
+ me = proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
29
+ if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
30
+ me.and_yield_receiver_to_implementation
23
31
  end
32
+
33
+ me
24
34
  end
25
35
 
26
36
  def invocation_order
@@ -2,55 +2,70 @@ module RSpec
2
2
  module Mocks
3
3
  module AnyInstance
4
4
  # @private
5
- class MessageChains < Hash
5
+ class MessageChains
6
6
  def initialize
7
- super {|h,k| h[k] = []}
7
+ @chains_by_method_name = Hash.new { |h, k| h[k] = [] }
8
+ end
9
+
10
+ # @private
11
+ def [](method_name)
12
+ @chains_by_method_name[method_name]
8
13
  end
9
14
 
10
15
  # @private
11
16
  def add(method_name, chain)
12
- self[method_name] << chain
17
+ @chains_by_method_name[method_name] << chain
13
18
  chain
14
19
  end
15
20
 
16
21
  # @private
17
22
  def remove_stub_chains_for!(method_name)
18
- self[method_name].reject! {|chain| chain.is_a?(StubChain)}
23
+ @chains_by_method_name[method_name].reject! do |chain|
24
+ StubChain === chain
25
+ end
19
26
  end
20
27
 
21
28
  # @private
22
29
  def has_expectation?(method_name)
23
- self[method_name].find {|chain| chain.is_a?(ExpectationChain)}
30
+ @chains_by_method_name[method_name].find do |chain|
31
+ ExpectationChain === chain
32
+ end
24
33
  end
25
34
 
26
35
  # @private
27
36
  def all_expectations_fulfilled?
28
- all? {|method_name, chains| chains.all? {|chain| chain.expectation_fulfilled?}}
37
+ @chains_by_method_name.all? do |method_name, chains|
38
+ chains.all? { |chain| chain.expectation_fulfilled? }
39
+ end
29
40
  end
30
41
 
31
42
  # @private
32
43
  def unfulfilled_expectations
33
- map do |method_name, chains|
34
- method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled?
44
+ @chains_by_method_name.map do |method_name, chains|
45
+ method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled?
35
46
  end.compact
36
47
  end
37
48
 
38
49
  # @private
39
50
  def received_expected_message!(method_name)
40
- self[method_name].each {|chain| chain.expectation_fulfilled!}
51
+ @chains_by_method_name[method_name].each do |chain|
52
+ chain.expectation_fulfilled!
53
+ end
41
54
  end
42
55
 
43
56
  # @private
44
57
  def playback!(instance, method_name)
45
58
  raise_if_second_instance_to_receive_message(instance)
46
- self[method_name].each {|chain| chain.playback!(instance)}
59
+ @chains_by_method_name[method_name].each do |chain|
60
+ chain.playback!(instance)
61
+ end
47
62
  end
48
63
 
49
64
  private
50
65
 
51
66
  def raise_if_second_instance_to_receive_message(instance)
52
- @instance_with_expectation ||= instance if instance.is_a?(ExpectationChain)
53
- if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance)
67
+ @instance_with_expectation ||= instance if ExpectationChain === instance
68
+ if ExpectationChain === instance && !@instance_with_expectation.equal?(instance)
54
69
  raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
55
70
  end
56
71
  end
@@ -11,7 +11,7 @@ module RSpec
11
11
  # @see Chain
12
12
  class Recorder
13
13
  # @private
14
- attr_reader :message_chains, :stubs
14
+ attr_reader :message_chains, :stubs, :klass
15
15
 
16
16
  def initialize(klass)
17
17
  @message_chains = MessageChains.new
@@ -27,7 +27,7 @@ module RSpec
27
27
  #
28
28
  # @see Methods#stub
29
29
  def stub(method_name_or_method_map, &block)
30
- if method_name_or_method_map.is_a?(Hash)
30
+ if Hash === method_name_or_method_map
31
31
  method_name_or_method_map.each do |method_name, return_value|
32
32
  stub(method_name).and_return(return_value)
33
33
  end
@@ -57,7 +57,7 @@ module RSpec
57
57
  def should_receive(method_name, &block)
58
58
  @expectation_set = true
59
59
  observe!(method_name)
60
- message_chains.add(method_name, ExpectationChain.new(self, method_name, &block))
60
+ message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block))
61
61
  end
62
62
 
63
63
  def should_not_receive(method_name, &block)
@@ -88,16 +88,9 @@ module RSpec
88
88
  if @expectation_set && !message_chains.all_expectations_fulfilled?
89
89
  raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
90
90
  end
91
- end
92
-
93
- # @private
94
- def stub!(*)
95
- raise "stub! is not supported on any_instance. Use stub instead."
96
- end
97
-
98
- # @private
99
- def unstub!(*)
100
- raise "unstub! is not supported on any_instance. Use unstub instead."
91
+ ensure
92
+ stop_all_observation!
93
+ ::RSpec::Mocks.space.remove_any_instance_recorder_for(@klass)
101
94
  end
102
95
 
103
96
  # @private
@@ -149,7 +142,7 @@ module RSpec
149
142
 
150
143
  def restore_original_method!(method_name)
151
144
  alias_method_name = build_alias_method_name(method_name)
152
- @klass.class_eval do
145
+ @klass.class_exec do
153
146
  remove_method method_name
154
147
  alias_method method_name, alias_method_name
155
148
  remove_method alias_method_name
@@ -157,14 +150,14 @@ module RSpec
157
150
  end
158
151
 
159
152
  def remove_dummy_method!(method_name)
160
- @klass.class_eval do
153
+ @klass.class_exec do
161
154
  remove_method method_name
162
155
  end
163
156
  end
164
157
 
165
158
  def backup_method!(method_name)
166
159
  alias_method_name = build_alias_method_name(method_name)
167
- @klass.class_eval do
160
+ @klass.class_exec do
168
161
  alias_method alias_method_name, method_name
169
162
  end if public_protected_or_private_method_defined?(method_name)
170
163
  end
@@ -179,28 +172,27 @@ module RSpec
179
172
  end
180
173
 
181
174
  def observe!(method_name)
175
+ if RSpec::Mocks.configuration.verify_partial_doubles?
176
+ raise MockExpectationError unless @klass.method_defined?(method_name)
177
+ end
178
+
182
179
  stop_observing!(method_name) if already_observing?(method_name)
183
180
  @observed_methods << method_name
184
181
  backup_method!(method_name)
185
- @klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1)
186
- def #{method_name}(*args, &blk)
187
- klass = ::RSpec::Mocks.method_handle_for(self, :#{method_name}).owner
188
- ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, :#{method_name})
189
- self.__send__(:#{method_name}, *args, &blk)
190
- end
191
- EOM
182
+ @klass.__send__(:define_method, method_name) do |*args, &blk|
183
+ klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner
184
+ ::RSpec::Mocks.any_instance_recorder_for(klass).playback!(self, method_name)
185
+ self.__send__(method_name, *args, &blk)
186
+ end
192
187
  end
193
188
 
194
189
  def mark_invoked!(method_name)
195
190
  backup_method!(method_name)
196
- @klass.class_eval(<<-EOM, __FILE__, __LINE__ + 1)
197
- def #{method_name}(*args, &blk)
198
- method_name = :#{method_name}
199
- klass = ::RSpec::Mocks.method_handle_for(self, :#{method_name}).owner
200
- invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name)
201
- raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by \#{self.inspect} but has already been received by \#{invoked_instance}"
202
- end
203
- EOM
191
+ @klass.__send__(:define_method, method_name) do |*args, &blk|
192
+ klass = ::RSpec::Mocks.method_handle_for(self, method_name).owner
193
+ invoked_instance = ::RSpec::Mocks.any_instance_recorder_for(klass).instance_that_received(method_name)
194
+ raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{self.inspect} but has already been received by #{invoked_instance}"
195
+ end
204
196
  end
205
197
  end
206
198
  end