1 /** 2 * Copyright the original author or authors. 3 * 4 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.mozilla.org/MPL/MPL-1.1.html 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import org.as2lib.core.BasicClass; 18 import org.as2lib.env.overload.Overload; 19 import org.as2lib.env.except.IllegalArgumentException; 20 import org.as2lib.env.reflect.ProxyFactory; 21 import org.as2lib.env.reflect.TypeProxyFactory; 22 import org.as2lib.env.reflect.InvocationHandler; 23 import org.as2lib.test.mock.ArgumentsMatcher; 24 import org.as2lib.test.mock.support.DefaultArgumentsMatcher; 25 import org.as2lib.test.mock.support.TypeArgumentsMatcher; 26 import org.as2lib.test.mock.Behavior; 27 import org.as2lib.test.mock.MethodCall; 28 import org.as2lib.test.mock.MethodCallRange; 29 import org.as2lib.test.mock.MethodResponse; 30 import org.as2lib.test.mock.support.DefaultBehavior; 31 import org.as2lib.test.mock.MockControlState; 32 import org.as2lib.test.mock.support.RecordState; 33 import org.as2lib.test.mock.support.ReplayState; 34 import org.as2lib.test.mock.MockControlStateFactory; 35 import org.as2lib.env.reflect.ReflectUtil; 36 37 /** 38 * {@code MockControl} is the central class of the mock object framework. You use 39 * it to create your mock object, set expectations and verify whether these 40 * expectations have been met. 41 * 42 * <p>The normal workflow is creating a mock control for a specific class or 43 * interface, receiving the mock object from it, setting expectations, setting the 44 * behavior of the mock object, switching to replay state, using the mock object as 45 * if it were a normal instance of a class and verifying that all expectations have 46 * been met. 47 * 48 * <code> 49 * import org.as2lib.test.mock.MockControl; 50 * 51 * // create mock control for class MyClass 52 * var myMockControl:MockControl = new MockControl(MyClass); 53 * // receive the mock object (it is in record state) 54 * var myMock:MyClass = myMockControl.getMock(); 55 * // expect a call to the setStringProperty-method with argument 'myString'. 56 * myMock.setStringProperty("myString"); 57 * // expect calls to the getStringProperty-method 58 * myMock.getStringProperty(); 59 * // return 'myString' for the first two calls 60 * myMockControl.setReturnValue("myString", 2); 61 * // throw MyException for any further call 62 * myMockControl.setDefaultThrowable(new MyException()); 63 * // switch to replay state 64 * myMockControl.replay(); 65 * 66 * // the class under test calls these methods on the mock 67 * myMock.setStringProperty("myString"); 68 * myMock.getStringProperty(); 69 * myMock.getStringProperty(); 70 * 71 * // verify that all expectations have been met 72 * myMockControl.verify(); 73 * </code> 74 * 75 * <p>If an expectation has not been met an {@link AssertionFailedError} will be 76 * thrown. If an expectation violation is discovered during execution an 77 * {@code AssertionFailedError} will be thrown immediately. 78 * 79 * <p>If you had called the {@code setStringProperty} method in the above example 80 * with another string like {@code "unexpectedString"} an {@code AssertFailedError} 81 * would have been thrown immediately. If you had called the {@code setStringProperty} 82 * method a second time, what has not been expected, an {@code AssertionFailedError} 83 * would also have been thrown immediately. If you had not called the 84 * {@code setStringProperty} method at all, an {@code AssertionFailedError} would 85 * have been thrown on verification. 86 * 87 * @author Simon Wacker 88 */ 89 class org.as2lib.test.mock.MockControl extends BasicClass { 90 91 /** 92 * Returns a new default arguments matcher. 93 * 94 * @return a new default arguments matcher 95 */ 96 public static function getDefaultArgumentsMatcher(Void):DefaultArgumentsMatcher { 97 return new DefaultArgumentsMatcher(); 98 } 99 100 /** 101 * Returns a new type arguments matcher that is configured with the passed-in 102 * {@code expectedType}. 103 * 104 * <p>Type arguments matcher matches arguments by type and not by value. 105 * 106 * @return a type arguments matcher 107 */ 108 public static function getTypeArgumentsMatcher(expectedTypes:Array):TypeArgumentsMatcher { 109 return new TypeArgumentsMatcher(expectedTypes); 110 } 111 112 /** The type of the mock proxy. */ 113 private var type:Function; 114 115 /** Used to create a new mock proxy. */ 116 private var proxyFactory:ProxyFactory; 117 118 /** The created mock proxy. */ 119 private var mock; 120 121 /** The mock behavior. */ 122 private var behavior:Behavior; 123 124 /** The current state. */ 125 private var state:MockControlState; 126 127 /** Factory used to obtain the record state. */ 128 private var recordStateFactory:MockControlStateFactory; 129 130 /** Factory used to obtain the replay state. */ 131 private var replayStateFactory:MockControlStateFactory; 132 133 /** Determines whether to handle {@code toString} method invocations. */ 134 private var handleToStringInvocations:Boolean; 135 136 /** 137 * @overload #MockControlByType 138 * @overload #MockControlByTypeAndBehavior 139 */ 140 public function MockControl() { 141 var o:Overload = new Overload(this); 142 o.addHandler([Function], MockControlByType); 143 o.addHandler([Function, Behavior], MockControlByTypeAndBehavior); 144 o.forward(arguments); 145 } 146 147 /** 148 * Constrcuts a new {@code MockControl} instance using the default behavior. 149 * 150 * <p>The default behavior is an instance of class {@link org.as2lib.test.mock.support.DefaultBehaviour}. 151 * 152 * <p>This instance is in reset state after creation. That means it is ready to 153 * receive expectations and to record them. 154 * 155 * <p>When you have finished recording you must switch to replay state using the 156 * {@link #replay} method. 157 * 158 * @param type the interface or class to create a mock object for 159 * @throws IllegalArgumentException if the passed-in {@code type} is {@code null} 160 */ 161 private function MockControlByType(type:Function):Void { 162 MockControlByTypeAndBehavior(type, null); 163 } 164 165 /** 166 * Constructs a new {@code MockControl} instance using the passed-in 167 * {@code bahvior}. 168 * 169 * <p>If the passed-in {@code behavior} is {@code null} the default behavior that 170 * is of type {@link DefaultBehavior} is used instead. 171 * 172 * <p>This instance is in reset state after creation. That means it is ready to 173 * to receive expectations and to record them. 174 * 175 * <p>When you have finished recording you must switch to replay state using the 176 * {@link #replay} method. 177 * 178 * <p>{@code toString} invocations on the mock are by default not handled. 179 * 180 * @param type the interface or class to create a mock object for 181 * @param behavior the instance to store the behavior of the mock 182 * @throws IllegalArgumentException if the passed-in {@code type} is {@code null} 183 * @see #setHandleToStringInvocations 184 */ 185 private function MockControlByTypeAndBehavior(type:Function, behavior:Behavior):Void { 186 if (!type) throw new IllegalArgumentException("The argument type '" + type + "' is not allowed to be null or undefined."); 187 this.type = type; 188 this.behavior = behavior ? behavior : new DefaultBehavior(); 189 this.handleToStringInvocations = false; 190 reset(); 191 } 192 193 /** 194 * Sets whether to handle {@code toString} invocations on mocks or not. 195 * 196 * <p>Handling {@code toString} invocations means that these invocations are 197 * added to the expected or actual behavior. This means if you set 198 * {@code handleToStringInvocations} to {@code true} calling this method on the 199 * mock in replay state results in an added expection and in record state in a 200 * verification whether the call was expected. If you set it to {@code false} the 201 * result of an invocation of the mock's {@code toString} method is returned. 202 * 203 * <p>If {@code handleToStringInvocations} is {@code null}, it is interpreted as 204 * {@code false}. 205 * 206 * @param handleToStringInvocations determines whether to handle {@code toStirng} 207 * method invocations 208 */ 209 public function setHandleToStringInvocations(handleToStringInvocations:Boolean):Void { 210 this.handleToStringInvocations = !handleToStringInvocations ? false : true; 211 } 212 213 /** 214 * Returns whether {@code toString} invocations on the mock are handled. 215 * 216 * <p>Handling {@code toString} invocations means that these invocations are 217 * added to the expected or actual behavior. This means if they are handled, 218 * calling the {@code toString} method on the mock in replay state results in an 219 * added expection and in record state in a verification whether the call was 220 * expected. If they are not handled, the result of an invocation of the mock's 221 * {@code toString} method is returned. 222 * 223 * @return {@code true} if {@code toString} invocations are handled else 224 * {@code false} 225 * @see #setHandleToStringInvocations 226 */ 227 public function areToStringInvocationsHandled(Void):Boolean { 228 return this.handleToStringInvocations; 229 } 230 231 /** 232 * Returns the currently used mock proxy factory. 233 * 234 * <p>This proxy factoy is either the default {@link TypeProxyFactory} or the one 235 * set via {@code setMockProxyFactory}. 236 * 237 * @return the currently used proxy factory 238 * @see #setMockProxyFactory 239 */ 240 public function getMockProxyFactory(Void):ProxyFactory { 241 if (!proxyFactory) proxyFactory = new TypeProxyFactory(); 242 return proxyFactory; 243 } 244 245 /** 246 * Sets the proxy factory used to obtain the mock proxis / mocks. 247 * 248 * <p>If {@code proxyFactory} is {@code null} the {@code getMockProxyFactory} 249 * method will use the default factory. 250 * 251 * @param proxyFactory factory to obtain mock proxies / mocks 252 * @see #getMockProxyFactory 253 */ 254 public function setMockProxyFactory(proxyFactory:ProxyFactory):Void { 255 this.proxyFactory = proxyFactory; 256 } 257 258 /** 259 * Returns the currently used record state factory. 260 * 261 * <p>This is either the factory set via {@code setRecordStateFactory} or the 262 * default one, which returns instances of the {@link RecordState} class. 263 * 264 * @return the currently used record state factory 265 * @see #setRecordStateFactory 266 */ 267 public function getRecordStateFactory(Void):MockControlStateFactory { 268 if (!recordStateFactory) recordStateFactory = getDefaultRecordStateFactory(); 269 return recordStateFactory; 270 } 271 272 /** 273 * Returns the default record state factory. 274 * 275 * <p>The default record state factory returns instances of class 276 * {@link RecordState}. 277 * 278 * @return the default record state factory 279 */ 280 private function getDefaultRecordStateFactory(Void):MockControlStateFactory { 281 var result:MockControlStateFactory = getBlankMockControlStateFactory(); 282 result.getMockControlState = function(behavior:Behavior):MockControlState { 283 return new RecordState(behavior); 284 }; 285 return result; 286 } 287 288 /** 289 * Sets the new record state factory. 290 * 291 * <p>If {@code recordStateFactory} is {@code null} the default record state 292 * factory gets returned by the {@code getRecordStateFactory} method. 293 * 294 * @param recordStateFactory the new record state factory 295 * @see #getRecordStateFactory 296 */ 297 public function setRecordStateFactory(recordStateFactory:MockControlStateFactory):Void { 298 this.recordStateFactory = recordStateFactory; 299 } 300 301 /** 302 * Returns the currently used replay state factory. 303 * 304 * <p>This is either the factory set via {@code setReplayStateFactory} or the 305 * default one, which returns instances of the {@link ReplayState} class. 306 * 307 * @return the currently used replay state factory 308 * @see #setReplayStateFactory 309 */ 310 public function getReplayStateFactory(Void):MockControlStateFactory { 311 if (!replayStateFactory) replayStateFactory = getDefaultReplayStateFactory(); 312 return replayStateFactory; 313 } 314 315 /** 316 * Returns the default replay state factory. 317 * 318 * <p>The default replay state factory returns instances of class 319 * {@link ReplayState}. 320 * 321 * @return the default replay state factory 322 */ 323 private function getDefaultReplayStateFactory(Void):MockControlStateFactory { 324 var result:MockControlStateFactory = getBlankMockControlStateFactory(); 325 result.getMockControlState = function(behavior:Behavior):MockControlState { 326 return new ReplayState(behavior); 327 }; 328 return result; 329 } 330 331 /** 332 * Sets the new replay state factory. 333 * 334 * <p>If {@code replayStateFactory} is {@code null} the 335 * {@code getReplayStateFactory} method will return the default replay state 336 * factory. 337 * 338 * @param replayStateFactory the new replay state factory 339 * @see #getReplayStateFactory 340 */ 341 public function setReplayStateFactory(replayStateFactory:MockControlStateFactory):Void { 342 this.replayStateFactory = replayStateFactory; 343 } 344 345 /** 346 * Returns a blank mock control state factory. That is a factory with no 347 * implemented methods. 348 * 349 * @return a blank mock control state factory 350 */ 351 private function getBlankMockControlStateFactory(Void):MockControlStateFactory { 352 var result = new Object(); 353 result.__proto__ = MockControlStateFactory["prototype"]; 354 result.__constructor__ = MockControlStateFactory; 355 return result; 356 } 357 358 /** 359 * Returns the mock object. 360 * 361 * <p>The mock can be casted and typed to the interface or class specified 362 * on instantiation. 363 * 364 * <p>The mock is created using the mock proxy factory returned by the 365 * {@link #getMockProxyFactory} method. 366 * 367 * <p>Once the mock object has been created it is cached. That means this method 368 * always returns the same mock object for this mock control. 369 * 370 * @return the mock object 371 */ 372 public function getMock(Void) { 373 if (!mock) mock = getMockProxyFactory().createProxy(type, createDelegator()); 374 return mock; 375 } 376 377 /** 378 * Creates a new invocation handler instance that handles method invocations on 379 * the mock proxy. 380 * 381 * @return a delegator that handles proxy method invocations 382 */ 383 private function createDelegator(Void):InvocationHandler { 384 var result:InvocationHandler = getBlankInvocationHandler(); 385 var owner:MockControl = this; 386 result.invoke = function(proxy, method:String, args:Array) { 387 // 'toString' must be excluded because it is used everytime output is made. 388 // For example in the success and failure messages of the unit testing api. 389 if (method == "toString" && !owner.areToStringInvocationsHandled()) { 390 // TODO: Source out into own stringifier class (MockStringifier) 391 return "[mock " + ReflectUtil.getTypeNameForInstance(owner.getMock()) + "]"; 392 //return owner.getMock().__proto__.toString.apply(owner.getMock()); 393 } 394 // calling private methods from an inner anonymous method is not allowed by MTASC 395 return owner["invokeMethod"](method, args); 396 }; 397 return result; 398 } 399 400 /** 401 * Returns a blank invocation handler. That is a handler with no implemented 402 * methods. 403 * 404 * @return a blank invocation handler 405 */ 406 private function getBlankInvocationHandler(Void):InvocationHandler { 407 var result = new Object(); 408 result.__proto__ = InvocationHandler["prototype"]; 409 result.__constructor__ = InvocationHandler; 410 return result; 411 } 412 413 /** 414 * Is called when a method is invoked on the proxy. 415 * 416 * @param methodName the name of the invoked method 417 * @param args the arguments passed to the invoked method 418 */ 419 private function invokeMethod(methodName:String, args:Array) { 420 // resolves bug with algorithms that check the existence of a method before 421 // they proceed; this is for example with the AsBroadcaster 422 var r:Function = mock.__resolve; 423 mock.__resolve = null; 424 if (!mock[methodName]) { 425 if (state instanceof RecordState) { 426 var owner:MockControl = this; 427 mock[methodName] = function() { 428 if (methodName == "toString" && !owner.areToStringInvocationsHandled()) { 429 return owner.getMock().__proto__.toString.apply(owner.getMock()); 430 } 431 // calling private methods out of inner anonymous methods is not allowed with MTASC 432 return owner["invokeMethod"](methodName, arguments); 433 }; 434 } 435 } 436 mock.__resolve = r; 437 var result; 438 try { 439 result = state.invokeMethod(new MethodCall(methodName, args)); 440 } catch(error:org.as2lib.test.mock.MethodCallRangeError) { 441 error.setType(type); 442 throw error; 443 } 444 return result; 445 } 446 447 /** 448 * Switches the mock object from record state to replay state. 449 * 450 * <p>The mock object is in record state as soon as it gets returned by the 451 * {@link #getMock} method. 452 * 453 * <p>You cannot record expectations in replay state. In replay state you verify 454 * that all your expectations have been met, by using the mock as it were a real 455 * instance. 456 * 457 * <p>If an expectations is not met an {@link AssertionFailedError} is thrown. 458 * This is either done during execution of your test or on verification. Take a 459 * look at the example provided in the class documentation to see when what 460 * {@code AssertFailedError} is thrown. 461 */ 462 public function replay(Void):Void { 463 state = getReplayStateFactory().getMockControlState(behavior); 464 } 465 466 /** 467 * Resets the mock control and the mock object to the state directly after 468 * creation. 469 * 470 * <p>That means that all previously made expectations will be removed and that 471 * the mock object will be again in record state. 472 */ 473 public function reset(Void):Void { 474 behavior.removeAllBehaviors(); 475 state = getRecordStateFactory().getMockControlState(behavior); 476 } 477 478 /** 479 * Sets the arguments matcher that will be used for the last method specified by 480 * a method call. 481 * 482 * @param argumentsMatcher the arguments matcher to use for the specific method 483 * @throws IllegalStateException if this mock control is in replay state 484 */ 485 public function setArgumentsMatcher(argumentsMatcher:ArgumentsMatcher):Void { 486 state.setArgumentsMatcher(argumentsMatcher); 487 } 488 489 /** 490 * Records that the mock object will by default allow the last method specified 491 * by a method call and will react by returning the provided return value. 492 * 493 * <p>Default means that the method can be called 0 to infinite times without 494 * expectation errors. 495 * 496 * @param value the return value to return 497 * @throws IllegalStateException if this mock control is in replay state 498 */ 499 public function setDefaultReturnValue(value):Void { 500 var response:MethodResponse = new MethodResponse(); 501 response.setReturnValue(value); 502 state.setMethodResponse(response, new MethodCallRange()); 503 } 504 505 /** 506 * Records that the mock object will by default allow the last method specified 507 * by a method call, and will react by throwing the provided throwable. 508 * 509 * <p>Default means that the method can be called zero to infinite times without 510 * expectation errors. 511 * 512 * @param throwable the throwable to throw 513 * @throws IllegalStateException if this mock control is in replay state 514 */ 515 public function setDefaultThrowable(throwable):Void { 516 var response:MethodResponse = new MethodResponse(); 517 response.setThrowable(throwable); 518 state.setMethodResponse(response, new MethodCallRange()); 519 } 520 521 /** 522 * Recards that the mock object will by default allow the last method specified 523 * by a method call. 524 * 525 * <p>Default means that the method can be called zero to infinite times without 526 * expectation errors. 527 * 528 * <p>Calling this method is not necessary. The mock control expects the last 529 * method specified by a method call as soon as this method call occured. 530 * 531 * @throws IllegalStateException if this mock control is in replay state 532 */ 533 public function setDefaultVoidCallable(Void):Void { 534 state.setMethodResponse(new MethodResponse(), new MethodCallRange()); 535 } 536 537 /** 538 * @overload #setReturnValueByValue 539 * @overload #setReturnValueByValueAndQuantity 540 * @overload #setReturnValueByValueAndMinimumAndMaximumQuantity 541 */ 542 public function setReturnValue():Void { 543 var o:Overload = new Overload(this); 544 o.addHandler([Object], setReturnValueByValue); 545 o.addHandler([Object, Number], setReturnValueByValueAndQuantity); 546 o.addHandler([Object, Number, Number], setReturnValueByValueAndMinimumAndMaximumQuantity); 547 o.forward(arguments); 548 } 549 550 /** 551 * Records that the mock object will expect the last method call once and will 552 * react by returning the provided return value. 553 * 554 * @param value the return value to return 555 * @throws IllegalStateException if this mock control is in replay state 556 */ 557 public function setReturnValueByValue(value):Void { 558 setReturnValueByValueAndQuantity(value, 1); 559 } 560 561 /** 562 * Records that the mock object will expect the last method call a fixed number 563 * of times and will react by returning the provided return value. 564 * 565 * @param value the return value to return 566 * @param quantity the number of times the method is allowed to be invoked 567 * @throws IllegalStateException if this mock control is in replay state 568 */ 569 public function setReturnValueByValueAndQuantity(value, quantity:Number):Void { 570 var response:MethodResponse = new MethodResponse(); 571 response.setReturnValue(value); 572 state.setMethodResponse(response, new MethodCallRange(quantity)); 573 } 574 575 /** 576 * Records that the mock object will expect the last method call between 577 * {@code minimumQuantity} and {@code maximumQuantity} times and will react by 578 * returning the provided return value. 579 * 580 * @param value the return value to return 581 * @param minimumQuantity the minimum number of times the method must be called 582 * @param maximumQuantity the maximum number of times the method can be called 583 * @throws IllegalStateException if this mock control is in replay state 584 */ 585 public function setReturnValueByValueAndMinimumAndMaximumQuantity(value, minimumQuantity:Number, maximumQuantity:Number):Void { 586 var response:MethodResponse = new MethodResponse(); 587 response.setReturnValue(value); 588 state.setMethodResponse(response, new MethodCallRange(minimumQuantity, maximumQuantity)); 589 } 590 591 /** 592 * @overload #setThrowableByThrowable 593 * @overload #setThrowableByThrowableAndQuantity 594 * @overload #setThrowableByThrowableAndMinimumAndMaximumQuantity 595 */ 596 public function setThrowable():Void { 597 var o:Overload = new Overload(this); 598 o.addHandler([Object], setThrowableByThrowable); 599 o.addHandler([Object, Number], setThrowableByThrowableAndQuantity); 600 o.addHandler([Object, Number, Number], setThrowableByThrowableAndMinimumAndMaximumQuantity); 601 o.forward(arguments); 602 } 603 604 /** 605 * Records that the mock object will expect the last method call once and will 606 * react by throwing the provided throwable. 607 * 608 * @param throwable the throwable to throw 609 * @throws IllegalStateException if this mock control is in replay state 610 */ 611 public function setThrowableByThrowable(throwable):Void { 612 setThrowableByThrowableAndQuantity(throwable, 1); 613 } 614 615 /** 616 * Records that the mock object will expect the last method call a fixed number 617 * of times and will react by throwing the provided throwable. 618 * 619 * @param throwable the throwable to throw 620 * @param quantity the number of times the method is allowed to be invoked 621 * @throws IllegalStateException if this mock control is in replay state 622 */ 623 public function setThrowableByThrowableAndQuantity(throwable, quantity:Number):Void { 624 var response:MethodResponse = new MethodResponse(); 625 response.setThrowable(throwable); 626 state.setMethodResponse(response, new MethodCallRange(quantity)); 627 } 628 629 /** 630 * Records that the mock object will expect the last method call between 631 * {@code minimumQuantity} and {@code maximumQuantity times} and will react by 632 * throwing the provided throwable. 633 * 634 * @param throwable the throwable to throw 635 * @param minimumQuantity the minimum number of times the method must be called 636 * @param maximumQuantity the maximum number of times the method can be called 637 * @throws IllegalStateException if this mock control is in replay state 638 */ 639 public function setThrowableByThrowableAndMinimumAndMaximumQuantity(throwable, minimumQuantity:Number, maximumQuantity:Number):Void { 640 var response:MethodResponse = new MethodResponse(); 641 response.setThrowable(throwable); 642 state.setMethodResponse(response, new MethodCallRange(minimumQuantity, maximumQuantity)); 643 } 644 645 /** 646 * @overload #setVoidCallableByVoid 647 * @overload #setVoidCallableByQuantity 648 * @overload #setVoidCallableByMinimumAndMaximumQuantity 649 */ 650 public function setVoidCallable():Void { 651 var o:Overload = new Overload(this); 652 o.addHandler([], setVoidCallableByVoid); 653 o.addHandler([Number], setVoidCallableByQuantity); 654 o.addHandler([Number, Number], setVoidCallableByMinimumAndMaximumQuantity); 655 o.forward(arguments); 656 } 657 658 /** 659 * Records that the mock object will expect the last method call once and will 660 * react by returning silently. 661 * 662 * @throws IllegalStateException if this mock control is in replay state 663 */ 664 public function setVoidCallableByVoid(Void):Void { 665 setVoidCallableByQuantity(1); 666 } 667 668 /** 669 * Records that the mock object will expect the last method call a fixed number 670 * of times and will react by returning silently. 671 * 672 * @param quantity the number of times the method is allowed to be invoked 673 * @throws IllegalStateException if this mock control is in replay state 674 */ 675 public function setVoidCallableByQuantity(quantity:Number):Void { 676 state.setMethodResponse(new MethodResponse(), new MethodCallRange(quantity)); 677 } 678 679 /** 680 * Records that the mock object will expect the last method call between 681 * {@code minimumQuantity} and {@code maximumQuantity} times and will react by 682 * returning silently. 683 * 684 * @param minimumQuantity the minimum number of times the method must be called 685 * @param maximumQuantity the maximum number of times the method can be called 686 * @throws IllegalStateException if this mock control is in replay state 687 */ 688 public function setVoidCallableByMinimumAndMaximumQuantity(minimumQuantity:Number, maximumQuantity:Number):Void { 689 state.setMethodResponse(new MethodResponse(), new MethodCallRange(minimumQuantity, maximumQuantity)); 690 } 691 692 /** 693 * Verifies that all expectations have been met that could not been verified 694 * during execution. 695 * 696 * @throws IllegalStateException if this mock control is in record state 697 * @throws AssertionFailedError if an expectation has not been met 698 */ 699 public function verify(Void):Void { 700 try { 701 state.verify(); 702 } catch(error:org.as2lib.test.mock.MethodCallRangeError) { 703 error.setType(type); 704 throw error; 705 } 706 } 707 708 }