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 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
|
module RR
module DoubleDefinitions
class DoubleDefinition #:nodoc:
ORIGINAL_METHOD = Object.new
attr_accessor(
:argument_expectation,
:times_matcher,
:implementation,
:after_call_proc,
:yields_value,
:double,
:double_definition_create
)
include Space::Reader
def initialize(double_definition_create)
@implementation = nil
@argument_expectation = nil
@times_matcher = nil
@after_call_proc = nil
@yields_value = nil
@double_definition_create = double_definition_create
@ordered = false
@verbose = false
@verify_method_signature = false
end
def subject
double_definition_create.subject
end
def root_subject
double_definition_create.root_subject
end
module ArgumentDefinitionConstructionMethods
if KeywordArguments.fully_supported?
# Double#with sets the expectation that the Double will receive
# the passed in arguments.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.with(1, 2) {:return_value}
def with(*args, **kwargs, &return_value_block)
@argument_expectation =
Expectations::ArgumentEqualityExpectation.new(args, kwargs)
install_method_callback return_value_block
self
end
else
def with(*args, &return_value_block)
@argument_expectation =
Expectations::ArgumentEqualityExpectation.new(args, {})
install_method_callback return_value_block
self
end
end
# Double#with_any_args sets the expectation that the Double can receive
# any arguments.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.with_any_args {:return_value}
def with_any_args(&return_value_block)
@argument_expectation = Expectations::AnyArgumentExpectation.new
install_method_callback return_value_block
self
end
# Double#with_no_args sets the expectation that the Double will receive
# no arguments.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.with_no_args {:return_value}
def with_no_args(&return_value_block)
@argument_expectation =
Expectations::ArgumentEqualityExpectation.new([], {})
install_method_callback return_value_block
self
end
end
include ArgumentDefinitionConstructionMethods
module TimesDefinitionConstructionMethods
# Double#never sets the expectation that the Double will never be
# called.
#
# This method does not accept a block because it will never be called.
#
# mock(subject).method_name.never
def never
@times_matcher = TimesCalledMatchers::NeverMatcher.new
self
end
# Double#once sets the expectation that the Double will be called
# 1 time.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.once {:return_value}
def once(&return_value_block)
@times_matcher = TimesCalledMatchers::IntegerMatcher.new(1)
install_method_callback return_value_block
self
end
# Double#twice sets the expectation that the Double will be called
# 2 times.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.twice {:return_value}
def twice(&return_value_block)
@times_matcher = TimesCalledMatchers::IntegerMatcher.new(2)
install_method_callback return_value_block
self
end
# Double#at_least sets the expectation that the Double
# will be called at least n times.
# It works by creating a TimesCalledExpectation.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.at_least(4) {:return_value}
def at_least(number, &return_value_block)
@times_matcher = TimesCalledMatchers::AtLeastMatcher.new(number)
install_method_callback return_value_block
self
end
# Double#at_most allows sets the expectation that the Double
# will be called at most n times.
# It works by creating a TimesCalledExpectation.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.at_most(4) {:return_value}
def at_most(number, &return_value_block)
@times_matcher = TimesCalledMatchers::AtMostMatcher.new(number)
install_method_callback return_value_block
self
end
# Double#any_number_of_times sets an that the Double will be called
# any number of times. This effectively removes the times called expectation
# from the Doublen
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.any_number_of_times
def any_number_of_times(&return_value_block)
@times_matcher = TimesCalledMatchers::AnyTimesMatcher.new
install_method_callback return_value_block
self
end
alias_method :any_times, :any_number_of_times
# Double#times creates an TimesCalledExpectation of the passed
# in number.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.times(4) {:return_value}
def times(matcher_value, &return_value_block)
@times_matcher = TimesCalledMatchers::TimesCalledMatcher.create(matcher_value)
install_method_callback return_value_block
self
end
end
include TimesDefinitionConstructionMethods
module DefinitionConstructionMethods
# Double#ordered sets the Double to have an ordered
# expectation.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.ordered {return_value}
def ordered(&return_value_block)
raise(
Errors::DoubleDefinitionError,
"Double Definitions must have a dedicated Double to be ordered. " <<
"For example, using instance_of does not allow ordered to be used. " <<
"proxy the class's #new method instead."
) unless @double
@ordered = true
space.register_ordered_double(@double)
install_method_callback return_value_block
DoubleDefinitionCreateBlankSlate.new(double_definition_create)
end
alias_method :then, :ordered
# Double#yields sets the Double to invoke a passed in block when
# the Double is called.
# An Expection will be raised if no block is passed in when the
# Double is called.
#
# Passing in a block sets the return value.
#
# mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
# subject.method_name {|yield_arg1, yield_arg2|}
def yields(*args, &return_value_block)
@yields_value = args
install_method_callback return_value_block
self
end
# Double#after_call creates a callback that occurs after call
# is called. The passed in block receives the return value of
# the Double being called.
# An Expection will be raised if no block is passed in.
#
# mock(subject).method_name {return_value}.after_call {|return_value|}
# subject.method_name # return_value
#
# This feature is built into proxies.
# mock.proxy(User).find('1') {|user| mock(user).valid? {false}}
def after_call(&after_call_proc)
raise ArgumentError, "after_call expects a block" unless after_call_proc
@after_call_proc = after_call_proc
self
end
# Double#verbose sets the Double to print out each method call it receives.
#
# Passing in a block sets the return value
def verbose(&after_call_proc)
@verbose = true
@after_call_proc = after_call_proc
self
end
# Double#returns accepts an argument value or a block.
# It will raise an ArgumentError if both are passed in.
#
# Passing in a block causes Double to return the return value of
# the passed in block.
#
# Passing in an argument causes Double to return the argument.
def returns(*args, &implementation)
if !args.empty? && implementation
raise ArgumentError, "returns cannot accept both an argument and a block"
end
if implementation
install_method_callback implementation
else
install_method_callback(lambda do |*lambda_args|
args.first
end)
end
self
end
def implemented_by_original_method
implemented_by ORIGINAL_METHOD
self
end
# Double#implemented_by sets the implementation of the Double.
# This method takes a Proc or a Method. Passing in a Method allows
# the Double to accept blocks.
#
# obj = Object.new
# def obj.foobar
# yield(1)
# end
# mock(obj).method_name.implemented_by(obj.method(:foobar))
def implemented_by(implementation)
@implementation = implementation
self
end
def verify_method_signature
@verify_method_signature = true
self
end
alias_method :strong, :verify_method_signature
protected
def install_method_callback(block)
if block
if implementation_is_original_method?
after_call(&block)
else
implemented_by block
end
end
end
end
include DefinitionConstructionMethods
module StateQueryMethods
# Double#ordered? returns true when the Double is ordered.
#
# mock(subject).method_name.ordered?
def ordered?
@ordered
end
# Double#verbose? returns true when verbose has been called on it. It returns
# true when the double is set to print each method call it receives.
def verbose?
@verbose ? true : false
end
def exact_match?(arguments, keyword_arguments)
unless @argument_expectation
raise RR::Errors.build_error(:DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}")
end
@argument_expectation.exact_match?(arguments, keyword_arguments)
end
def wildcard_match?(arguments, keyword_arguments)
unless @argument_expectation
raise RR::Errors.build_error(:DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}")
end
@argument_expectation.wildcard_match?(arguments, keyword_arguments)
end
def terminal?
unless @times_matcher
raise RR::Errors.build_error(:DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}")
end
@times_matcher.terminal?
end
def expected_arguments
if argument_expectation
argument_expectation.expected_arguments
else
[]
end
end
def expected_keyword_arguments
if argument_expectation
argument_expectation.expected_keyword_arguments
else
{}
end
end
def implementation_is_original_method?
implementation_strategy.is_a?(Strategies::Implementation::Proxy)
end
def verify_method_signature?
!!@verify_method_signature
end
alias_method :strong?, :verify_method_signature?
protected
def implementation_strategy
double_definition_create.implementation_strategy
end
end
include StateQueryMethods
include ::RR::DoubleDefinitions::Strategies::StrategyMethods
def mock(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).mock(subject, method_name, &definition_eval_block)
end
def stub(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).stub(subject, method_name, &definition_eval_block)
end
def dont_allow(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).dont_allow(subject, method_name, &definition_eval_block)
end
def proxy(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).proxy(subject, method_name, &definition_eval_block)
end
def strong(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).strong(subject, method_name, &definition_eval_block)
end
def any_instance_of(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).any_instance_of(subject, method_name, &definition_eval_block)
end
def instance_of(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
ChildDoubleDefinitionCreate.new(self).instance_of(subject, method_name, &definition_eval_block)
end
end
end
end
|