rspec-mocks 3.8.1
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +3 -0
- data/.document +5 -0
- data/.yardopts +6 -0
- data/Changelog.md +1108 -0
- data/LICENSE.md +25 -0
- data/README.md +460 -0
- data/lib/rspec/mocks.rb +130 -0
- data/lib/rspec/mocks/any_instance.rb +11 -0
- data/lib/rspec/mocks/any_instance/chain.rb +110 -0
- data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +50 -0
- data/lib/rspec/mocks/any_instance/message_chains.rb +83 -0
- data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
- data/lib/rspec/mocks/any_instance/recorder.rb +289 -0
- data/lib/rspec/mocks/any_instance/stub_chain.rb +51 -0
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +100 -0
- data/lib/rspec/mocks/argument_matchers.rb +320 -0
- data/lib/rspec/mocks/configuration.rb +212 -0
- data/lib/rspec/mocks/error_generator.rb +369 -0
- data/lib/rspec/mocks/example_methods.rb +434 -0
- data/lib/rspec/mocks/instance_method_stasher.rb +146 -0
- data/lib/rspec/mocks/marshal_extension.rb +41 -0
- data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
- data/lib/rspec/mocks/matchers/have_received.rb +134 -0
- data/lib/rspec/mocks/matchers/receive.rb +132 -0
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +82 -0
- data/lib/rspec/mocks/matchers/receive_messages.rb +77 -0
- data/lib/rspec/mocks/message_chain.rb +87 -0
- data/lib/rspec/mocks/message_expectation.rb +741 -0
- data/lib/rspec/mocks/method_double.rb +287 -0
- data/lib/rspec/mocks/method_reference.rb +202 -0
- data/lib/rspec/mocks/minitest_integration.rb +68 -0
- data/lib/rspec/mocks/mutate_const.rb +339 -0
- data/lib/rspec/mocks/object_reference.rb +149 -0
- data/lib/rspec/mocks/order_group.rb +81 -0
- data/lib/rspec/mocks/proxy.rb +485 -0
- data/lib/rspec/mocks/space.rb +238 -0
- data/lib/rspec/mocks/standalone.rb +3 -0
- data/lib/rspec/mocks/syntax.rb +325 -0
- data/lib/rspec/mocks/targets.rb +124 -0
- data/lib/rspec/mocks/test_double.rb +171 -0
- data/lib/rspec/mocks/verifying_double.rb +129 -0
- data/lib/rspec/mocks/verifying_message_expectation.rb +54 -0
- data/lib/rspec/mocks/verifying_proxy.rb +220 -0
- data/lib/rspec/mocks/version.rb +9 -0
- metadata +221 -0
- metadata.gz.sig +0 -0
data/lib/rspec/mocks.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'rspec/support'
|
2
|
+
RSpec::Support.require_rspec_support 'caller_filter'
|
3
|
+
RSpec::Support.require_rspec_support 'warnings'
|
4
|
+
RSpec::Support.require_rspec_support 'ruby_features'
|
5
|
+
|
6
|
+
RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f }
|
7
|
+
|
8
|
+
%w[
|
9
|
+
instance_method_stasher
|
10
|
+
method_double
|
11
|
+
argument_matchers
|
12
|
+
example_methods
|
13
|
+
proxy
|
14
|
+
test_double
|
15
|
+
argument_list_matcher
|
16
|
+
message_expectation
|
17
|
+
order_group
|
18
|
+
error_generator
|
19
|
+
space
|
20
|
+
mutate_const
|
21
|
+
targets
|
22
|
+
syntax
|
23
|
+
configuration
|
24
|
+
verifying_double
|
25
|
+
version
|
26
|
+
].each { |name| RSpec::Support.require_rspec_mocks name }
|
27
|
+
|
28
|
+
# Share the top-level RSpec namespace, because we are a core supported
|
29
|
+
# extension.
|
30
|
+
module RSpec
|
31
|
+
# Contains top-level utility methods. While this contains a few
|
32
|
+
# public methods, these are not generally meant to be called from
|
33
|
+
# a test or example. They exist primarily for integration with
|
34
|
+
# test frameworks (such as rspec-core).
|
35
|
+
module Mocks
|
36
|
+
# Performs per-test/example setup. This should be called before
|
37
|
+
# an test or example begins.
|
38
|
+
def self.setup
|
39
|
+
@space_stack << (@space = space.new_scope)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Verifies any message expectations that were set during the
|
43
|
+
# test or example. This should be called at the end of an example.
|
44
|
+
def self.verify
|
45
|
+
space.verify_all
|
46
|
+
end
|
47
|
+
|
48
|
+
# Cleans up all test double state (including any methods that were
|
49
|
+
# redefined on partial doubles). This _must_ be called after
|
50
|
+
# each example, even if an error was raised during the example.
|
51
|
+
def self.teardown
|
52
|
+
space.reset_all
|
53
|
+
@space_stack.pop
|
54
|
+
@space = @space_stack.last || @root_space
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds an allowance (stub) on `subject`
|
58
|
+
#
|
59
|
+
# @param subject the subject to which the message will be added
|
60
|
+
# @param message a symbol, representing the message that will be
|
61
|
+
# added.
|
62
|
+
# @param opts a hash of options, :expected_from is used to set the
|
63
|
+
# original call site
|
64
|
+
# @yield an optional implementation for the allowance
|
65
|
+
#
|
66
|
+
# @example Defines the implementation of `foo` on `bar`, using the passed block
|
67
|
+
# x = 0
|
68
|
+
# RSpec::Mocks.allow_message(bar, :foo) { x += 1 }
|
69
|
+
def self.allow_message(subject, message, opts={}, &block)
|
70
|
+
space.proxy_for(subject).add_stub(message, opts, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets a message expectation on `subject`.
|
74
|
+
# @param subject the subject on which the message will be expected
|
75
|
+
# @param message a symbol, representing the message that will be
|
76
|
+
# expected.
|
77
|
+
# @param opts a hash of options, :expected_from is used to set the
|
78
|
+
# original call site
|
79
|
+
# @yield an optional implementation for the expectation
|
80
|
+
#
|
81
|
+
# @example Expect the message `foo` to receive `bar`, then call it
|
82
|
+
# RSpec::Mocks.expect_message(bar, :foo)
|
83
|
+
# bar.foo
|
84
|
+
def self.expect_message(subject, message, opts={}, &block)
|
85
|
+
space.proxy_for(subject).add_message_expectation(message, opts, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Call the passed block and verify mocks after it has executed. This allows
|
89
|
+
# mock usage in arbitrary places, such as a `before(:all)` hook.
|
90
|
+
def self.with_temporary_scope
|
91
|
+
setup
|
92
|
+
|
93
|
+
begin
|
94
|
+
yield
|
95
|
+
verify
|
96
|
+
ensure
|
97
|
+
teardown
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class << self
|
102
|
+
# @private
|
103
|
+
attr_reader :space
|
104
|
+
end
|
105
|
+
@space_stack = []
|
106
|
+
@root_space = @space = RSpec::Mocks::RootSpace.new
|
107
|
+
|
108
|
+
# @private
|
109
|
+
IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
|
110
|
+
|
111
|
+
# To speed up boot time a bit, delay loading optional or rarely
|
112
|
+
# used features until their first use.
|
113
|
+
autoload :AnyInstance, "rspec/mocks/any_instance"
|
114
|
+
autoload :ExpectChain, "rspec/mocks/message_chain"
|
115
|
+
autoload :StubChain, "rspec/mocks/message_chain"
|
116
|
+
autoload :MarshalExtension, "rspec/mocks/marshal_extension"
|
117
|
+
|
118
|
+
# Namespace for mock-related matchers.
|
119
|
+
module Matchers
|
120
|
+
# @private
|
121
|
+
# just a "tag" for rspec-mock matchers detection
|
122
|
+
module Matcher; end
|
123
|
+
|
124
|
+
autoload :HaveReceived, "rspec/mocks/matchers/have_received"
|
125
|
+
autoload :Receive, "rspec/mocks/matchers/receive"
|
126
|
+
autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain"
|
127
|
+
autoload :ReceiveMessages, "rspec/mocks/matchers/receive_messages"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
%w[
|
2
|
+
any_instance/chain
|
3
|
+
any_instance/error_generator
|
4
|
+
any_instance/stub_chain
|
5
|
+
any_instance/stub_chain_chain
|
6
|
+
any_instance/expect_chain_chain
|
7
|
+
any_instance/expectation_chain
|
8
|
+
any_instance/message_chains
|
9
|
+
any_instance/recorder
|
10
|
+
any_instance/proxy
|
11
|
+
].each { |f| RSpec::Support.require_rspec_mocks(f) }
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
# @private
|
4
|
+
module AnyInstance
|
5
|
+
# @private
|
6
|
+
class Chain
|
7
|
+
def initialize(recorder, *args, &block)
|
8
|
+
@recorder = recorder
|
9
|
+
@expectation_args = args
|
10
|
+
@expectation_block = block
|
11
|
+
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
12
|
+
end
|
13
|
+
|
14
|
+
# @private
|
15
|
+
#
|
16
|
+
# Provides convenience methods for recording customizations on message
|
17
|
+
# expectations.
|
18
|
+
module Customizations
|
19
|
+
# @macro [attach] record
|
20
|
+
# @method $1(*args, &block)
|
21
|
+
# Records the `$1` message for playback against an instance that
|
22
|
+
# invokes a method stubbed or mocked using `any_instance`.
|
23
|
+
#
|
24
|
+
# @see RSpec::Mocks::MessageExpectation#$1
|
25
|
+
#
|
26
|
+
def self.record(method_name)
|
27
|
+
define_method(method_name) do |*args, &block|
|
28
|
+
record(method_name, *args, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
record :and_return
|
33
|
+
record :and_raise
|
34
|
+
record :and_throw
|
35
|
+
record :and_yield
|
36
|
+
record :and_call_original
|
37
|
+
record :and_wrap_original
|
38
|
+
record :with
|
39
|
+
record :once
|
40
|
+
record :twice
|
41
|
+
record :thrice
|
42
|
+
record :exactly
|
43
|
+
record :times
|
44
|
+
record :never
|
45
|
+
record :at_least
|
46
|
+
record :at_most
|
47
|
+
end
|
48
|
+
|
49
|
+
include Customizations
|
50
|
+
|
51
|
+
# @private
|
52
|
+
def playback!(instance)
|
53
|
+
message_expectation = create_message_expectation_on(instance)
|
54
|
+
messages.inject(message_expectation) do |object, message|
|
55
|
+
object.__send__(*message.first, &message.last)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @private
|
60
|
+
def constrained_to_any_of?(*constraints)
|
61
|
+
constraints.any? do |constraint|
|
62
|
+
messages.any? do |message|
|
63
|
+
message.first.first == constraint
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private
|
69
|
+
def matches_args?(*args)
|
70
|
+
@argument_list_matcher.args_match?(*args)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @private
|
74
|
+
def expectation_fulfilled!
|
75
|
+
@expectation_fulfilled = true
|
76
|
+
end
|
77
|
+
|
78
|
+
def never
|
79
|
+
AnyInstance.error_generator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated?
|
80
|
+
super
|
81
|
+
end
|
82
|
+
|
83
|
+
def with(*args, &block)
|
84
|
+
@argument_list_matcher = ArgumentListMatcher.new(*args)
|
85
|
+
super
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def negated?
|
91
|
+
messages.any? { |(message, *_), _| message == :never }
|
92
|
+
end
|
93
|
+
|
94
|
+
def messages
|
95
|
+
@messages ||= []
|
96
|
+
end
|
97
|
+
|
98
|
+
def last_message
|
99
|
+
messages.last.first.first unless messages.empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
def record(rspec_method_name, *args, &block)
|
103
|
+
verify_invocation_order(rspec_method_name, *args, &block)
|
104
|
+
messages << [args.unshift(rspec_method_name), block]
|
105
|
+
self
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module AnyInstance
|
4
|
+
# @private
|
5
|
+
class ErrorGenerator < ::RSpec::Mocks::ErrorGenerator
|
6
|
+
def raise_second_instance_received_message_error(unfulfilled_expectations)
|
7
|
+
__raise "Exactly one instance should have received the following " \
|
8
|
+
"message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def raise_does_not_implement_error(klass, method_name)
|
12
|
+
__raise "#{klass} does not implement ##{method_name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def raise_message_already_received_by_other_instance_error(method_name, object_inspect, invoked_instance)
|
16
|
+
__raise "The message '#{method_name}' was received by #{object_inspect} " \
|
17
|
+
"but has already been received by #{invoked_instance}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def raise_not_supported_with_prepend_error(method_name, problem_mod)
|
21
|
+
__raise "Using `any_instance` to stub a method (#{method_name}) that has been " \
|
22
|
+
"defined on a prepended module (#{problem_mod}) is not supported."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.error_generator
|
27
|
+
@error_generator ||= ErrorGenerator.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module AnyInstance
|
4
|
+
# @private
|
5
|
+
class ExpectChainChain < StubChain
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@expectation_fulfilled = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def expectation_fulfilled?
|
12
|
+
@expectation_fulfilled
|
13
|
+
end
|
14
|
+
|
15
|
+
def playback!(instance)
|
16
|
+
super.tap { @expectation_fulfilled = true }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def create_message_expectation_on(instance)
|
22
|
+
::RSpec::Mocks::ExpectChain.expect_chain_on(instance, *@expectation_args, &@expectation_block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def invocation_order
|
26
|
+
EmptyInvocationOrder
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module AnyInstance
|
4
|
+
# @private
|
5
|
+
class ExpectationChain < Chain
|
6
|
+
def expectation_fulfilled?
|
7
|
+
@expectation_fulfilled || constrained_to_any_of?(:never)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(*args, &block)
|
11
|
+
@expectation_fulfilled = false
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def verify_invocation_order(_rspec_method_name, *_args, &_block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @private
|
22
|
+
class PositiveExpectationChain < ExpectationChain
|
23
|
+
private
|
24
|
+
|
25
|
+
def create_message_expectation_on(instance)
|
26
|
+
proxy = ::RSpec::Mocks.space.proxy_for(instance)
|
27
|
+
method_name, opts = @expectation_args
|
28
|
+
opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE)
|
29
|
+
|
30
|
+
me = proxy.add_message_expectation(method_name, opts, &@expectation_block)
|
31
|
+
if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
|
32
|
+
me.and_yield_receiver_to_implementation
|
33
|
+
end
|
34
|
+
|
35
|
+
me
|
36
|
+
end
|
37
|
+
|
38
|
+
ExpectationInvocationOrder =
|
39
|
+
{
|
40
|
+
:and_return => [:with, nil],
|
41
|
+
:and_raise => [:with, nil],
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
def invocation_order
|
45
|
+
ExpectationInvocationOrder
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
module AnyInstance
|
4
|
+
# @private
|
5
|
+
class MessageChains
|
6
|
+
def initialize
|
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]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @private
|
16
|
+
def add(method_name, chain)
|
17
|
+
@chains_by_method_name[method_name] << chain
|
18
|
+
chain
|
19
|
+
end
|
20
|
+
|
21
|
+
# @private
|
22
|
+
def remove_stub_chains_for!(method_name)
|
23
|
+
@chains_by_method_name[method_name].reject! do |chain|
|
24
|
+
StubChain === chain
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @private
|
29
|
+
def has_expectation?(method_name)
|
30
|
+
@chains_by_method_name[method_name].find do |chain|
|
31
|
+
ExpectationChain === chain
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @private
|
36
|
+
def each_unfulfilled_expectation_matching(method_name, *args)
|
37
|
+
@chains_by_method_name[method_name].each do |chain|
|
38
|
+
yield chain if !chain.expectation_fulfilled? && chain.matches_args?(*args)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private
|
43
|
+
def all_expectations_fulfilled?
|
44
|
+
@chains_by_method_name.all? do |_method_name, chains|
|
45
|
+
chains.all? { |chain| chain.expectation_fulfilled? }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @private
|
50
|
+
def unfulfilled_expectations
|
51
|
+
@chains_by_method_name.map do |method_name, chains|
|
52
|
+
method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled?
|
53
|
+
end.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
# @private
|
57
|
+
def received_expected_message!(method_name)
|
58
|
+
@chains_by_method_name[method_name].each do |chain|
|
59
|
+
chain.expectation_fulfilled!
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @private
|
64
|
+
def playback!(instance, method_name)
|
65
|
+
raise_if_second_instance_to_receive_message(instance)
|
66
|
+
@chains_by_method_name[method_name].each do |chain|
|
67
|
+
chain.playback!(instance)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def raise_if_second_instance_to_receive_message(instance)
|
74
|
+
@instance_with_expectation ||= instance if ExpectationChain === instance
|
75
|
+
return unless ExpectationChain === instance
|
76
|
+
return if @instance_with_expectation.equal?(instance)
|
77
|
+
|
78
|
+
AnyInstance.error_generator.raise_second_instance_received_message_error(unfulfilled_expectations)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|