1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
module RSpec
module Mocks
# @private
class Proxy
# @private
def initialize(object, name=nil, options={})
@object = object
@name = name
@error_generator = ErrorGenerator.new object, name, options
@expectation_ordering = RSpec::Mocks::space.expectation_ordering
@messages_received = []
@options = options
@already_proxied_respond_to = false
@null_object = false
end
# @private
attr_reader :object
# @private
def null_object?
@null_object
end
# @private
# Tells the object to ignore any messages that aren't explicitly set as
# stubs or message expectations.
def as_null_object
@null_object = true
@object
end
# @private
def already_proxied_respond_to
@already_proxied_respond_to = true
end
# @private
def already_proxied_respond_to?
@already_proxied_respond_to
end
# @private
def add_message_expectation(location, method_name, opts={}, &block)
meth_double = method_double[method_name]
if null_object? && !block
meth_double.add_default_stub(@error_generator, @expectation_ordering, location, opts) do
@object
end
end
meth_double.add_expectation @error_generator, @expectation_ordering, location, opts, &block
end
# @private
def build_expectation(method_name)
meth_double = method_double[method_name]
meth_double.build_expectation(
@error_generator,
@expectation_ordering
)
end
# @private
def replay_received_message_on(expectation)
expected_method_name = expectation.message
meth_double = method_double[expected_method_name]
if meth_double.expectations.any?
@error_generator.raise_expectation_on_mocked_method(expected_method_name)
end
unless null_object? || meth_double.stubs.any?
@error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
end
@messages_received.each do |(actual_method_name, args, _)|
if expectation.matches?(actual_method_name, *args)
expectation.invoke(nil)
end
end
end
# @private
def check_for_unexpected_arguments(expectation)
@messages_received.each do |(method_name, args, _)|
if expectation.matches_name_but_not_args(method_name, *args)
raise_unexpected_message_args_error(expectation, *args)
end
end
end
# @private
def add_stub(location, method_name, opts={}, &implementation)
method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation
end
# @private
def remove_stub(method_name)
method_double[method_name].remove_stub
end
def remove_single_stub(method_name, stub)
method_double[method_name].remove_single_stub(stub)
end
# @private
def verify
method_doubles.each {|d| d.verify}
ensure
reset
end
# @private
def reset
method_doubles.each {|d| d.reset}
@messages_received.clear
end
# @private
def received_message?(method_name, *args, &block)
@messages_received.any? {|array| array == [method_name, args, block]}
end
# @private
def has_negative_expectation?(message)
method_double[message].expectations.detect {|expectation| expectation.negative_expectation_for?(message)}
end
# @private
def record_message_received(message, *args, &block)
@messages_received << [message, args, block]
end
# @private
def message_received(message, *args, &block)
record_message_received message, *args, &block
expectation = find_matching_expectation(message, *args)
stub = find_matching_method_stub(message, *args)
if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters?
if expectation = find_almost_matching_expectation(message, *args)
expectation.advise(*args) unless expectation.expected_messages_received?
end
stub.invoke(nil, *args, &block)
elsif expectation
expectation.invoke(stub, *args, &block)
elsif expectation = find_almost_matching_expectation(message, *args)
expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
raise_unexpected_message_args_error(expectation, *args) unless (has_negative_expectation?(message) or null_object?)
elsif stub = find_almost_matching_stub(message, *args)
stub.advise(*args)
raise_missing_default_stub_error(stub, *args)
elsif @object.is_a?(Class)
@object.superclass.__send__(message, *args, &block)
else
@object.__send__(:method_missing, message, *args, &block)
end
end
# @private
def raise_unexpected_message_error(method_name, *args)
@error_generator.raise_unexpected_message_error method_name, *args
end
# @private
def raise_unexpected_message_args_error(expectation, *args)
@error_generator.raise_unexpected_message_args_error(expectation, *args)
end
# @private
def raise_missing_default_stub_error(expectation, *args)
@error_generator.raise_missing_default_stub_error(expectation, *args)
end
private
def method_double
@method_double ||= Hash.new {|h,k| h[k] = MethodDouble.new(@object, k, self) }
end
def method_doubles
method_double.values
end
def find_matching_expectation(method_name, *args)
find_best_matching_expectation_for(method_name) do |expectation|
expectation.matches?(method_name, *args)
end
end
def find_almost_matching_expectation(method_name, *args)
find_best_matching_expectation_for(method_name) do |expectation|
expectation.matches_name_but_not_args(method_name, *args)
end
end
def find_best_matching_expectation_for(method_name)
first_match = nil
method_double[method_name].expectations.each do |expectation|
next unless yield expectation
return expectation unless expectation.called_max_times?
first_match ||= expectation
end
first_match
end
def find_matching_method_stub(method_name, *args)
method_double[method_name].stubs.find {|stub| stub.matches?(method_name, *args)}
end
def find_almost_matching_stub(method_name, *args)
method_double[method_name].stubs.find {|stub| stub.matches_name_but_not_args(method_name, *args)}
end
end
end
end
|