3 * base include file for SimpleTest
5 * @subpackage MockObjects
10 * include SimpleTest files
12 require_once(dirname(__FILE__
) . '/expectation.php');
13 require_once(dirname(__FILE__
) . '/simpletest.php');
14 require_once(dirname(__FILE__
) . '/dumper.php');
15 if (version_compare(phpversion(), '5') >= 0) {
16 require_once(dirname(__FILE__
) . '/reflection_php5.php');
18 require_once(dirname(__FILE__
) . '/reflection_php4.php');
23 * Default character simpletest will substitute for any value
25 if (! defined('MOCK_ANYTHING')) {
26 define('MOCK_ANYTHING', '*');
30 * Parameter comparison assertion.
32 * @subpackage MockObjects
34 class ParametersExpectation
extends SimpleExpectation
{
38 * Sets the expected parameter list.
39 * @param array $parameters Array of parameters including
40 * those that are wildcarded.
41 * If the value is not an array
42 * then it is considered to match any.
43 * @param string $message Customised message on failure.
46 function ParametersExpectation($expected = false, $message = '%s') {
47 $this->SimpleExpectation($message);
48 $this->_expected
= $expected;
52 * Tests the assertion. True if correct.
53 * @param array $parameters Comparison values.
54 * @return boolean True if correct.
57 function test($parameters) {
58 if (! is_array($this->_expected
)) {
61 if (count($this->_expected
) != count($parameters)) {
64 for ($i = 0; $i < count($this->_expected
); $i++
) {
65 if (! $this->_testParameter($parameters[$i], $this->_expected
[$i])) {
73 * Tests an individual parameter.
74 * @param mixed $parameter Value to test.
75 * @param mixed $expected Comparison value.
76 * @return boolean True if expectation
80 function _testParameter($parameter, $expected) {
81 $comparison = $this->_coerceToExpectation($expected);
82 return $comparison->test($parameter);
86 * Returns a human readable test message.
87 * @param array $comparison Incoming parameter list.
88 * @return string Description of success
92 function testMessage($parameters) {
93 if ($this->test($parameters)) {
94 return "Expectation of " . count($this->_expected
) .
95 " arguments of [" . $this->_renderArguments($this->_expected
) .
98 return $this->_describeDifference($this->_expected
, $parameters);
103 * Message to display if expectation differs from
104 * the parameters actually received.
105 * @param array $expected Expected parameters as list.
106 * @param array $parameters Actual parameters received.
107 * @return string Description of difference.
110 function _describeDifference($expected, $parameters) {
111 if (count($expected) != count($parameters)) {
112 return "Expected " . count($expected) .
113 " arguments of [" . $this->_renderArguments($expected) .
114 "] but got " . count($parameters) .
115 " arguments of [" . $this->_renderArguments($parameters) . "]";
118 for ($i = 0; $i < count($expected); $i++
) {
119 $comparison = $this->_coerceToExpectation($expected[$i]);
120 if (! $comparison->test($parameters[$i])) {
121 $messages[] = "parameter " . ($i +
1) . " with [" .
122 $comparison->overlayMessage($parameters[$i], $this->_getDumper()) . "]";
125 return "Parameter expectation differs at " . implode(" and ", $messages);
129 * Creates an identical expectation if the
130 * object/value is not already some type
132 * @param mixed $expected Expected value.
133 * @return SimpleExpectation Expectation object.
136 function _coerceToExpectation($expected) {
137 if (SimpleExpectation
::isExpectation($expected)) {
140 return new IdenticalExpectation($expected);
144 * Renders the argument list as a string for
146 * @param array $args Incoming arguments.
147 * @return string Simple description of type and value.
150 function _renderArguments($args) {
151 $descriptions = array();
152 if (is_array($args)) {
153 foreach ($args as $arg) {
154 $dumper = new SimpleDumper();
155 $descriptions[] = $dumper->describeValue($arg);
158 return implode(', ', $descriptions);
163 * Confirms that the number of calls on a method is as expected.
164 * @package SimpleTest
165 * @subpackage MockObjects
167 class CallCountExpectation
extends SimpleExpectation
{
172 * Stashes the method and expected count for later
174 * @param string $method Name of method to confirm against.
175 * @param integer $count Expected number of calls.
176 * @param string $message Custom error message.
178 function CallCountExpectation($method, $count, $message = '%s') {
179 $this->_method
= $method;
180 $this->_count
= $count;
181 $this->SimpleExpectation($message);
185 * Tests the assertion. True if correct.
186 * @param integer $compare Measured call count.
187 * @return boolean True if expected.
190 function test($compare) {
191 return ($this->_count
== $compare);
195 * Reports the comparison.
196 * @param integer $compare Measured call count.
197 * @return string Message to show.
200 function testMessage($compare) {
201 return 'Expected call count for [' . $this->_method
.
202 '] was [' . $this->_count
.
203 '] got [' . $compare . ']';
208 * Confirms that the number of calls on a method is as expected.
209 * @package SimpleTest
210 * @subpackage MockObjects
212 class MinimumCallCountExpectation
extends SimpleExpectation
{
217 * Stashes the method and expected count for later
219 * @param string $method Name of method to confirm against.
220 * @param integer $count Minimum number of calls.
221 * @param string $message Custom error message.
223 function MinimumCallCountExpectation($method, $count, $message = '%s') {
224 $this->_method
= $method;
225 $this->_count
= $count;
226 $this->SimpleExpectation($message);
230 * Tests the assertion. True if correct.
231 * @param integer $compare Measured call count.
232 * @return boolean True if enough.
235 function test($compare) {
236 return ($this->_count
<= $compare);
240 * Reports the comparison.
241 * @param integer $compare Measured call count.
242 * @return string Message to show.
245 function testMessage($compare) {
246 return 'Minimum call count for [' . $this->_method
.
247 '] was [' . $this->_count
.
248 '] got [' . $compare . ']';
253 * Confirms that the number of calls on a method is as expected.
254 * @package SimpleTest
255 * @subpackage MockObjects
257 class MaximumCallCountExpectation
extends SimpleExpectation
{
262 * Stashes the method and expected count for later
264 * @param string $method Name of method to confirm against.
265 * @param integer $count Minimum number of calls.
266 * @param string $message Custom error message.
268 function MaximumCallCountExpectation($method, $count, $message = '%s') {
269 $this->_method
= $method;
270 $this->_count
= $count;
271 $this->SimpleExpectation($message);
275 * Tests the assertion. True if correct.
276 * @param integer $compare Measured call count.
277 * @return boolean True if not over.
280 function test($compare) {
281 return ($this->_count
>= $compare);
285 * Reports the comparison.
286 * @param integer $compare Measured call count.
287 * @return string Message to show.
290 function testMessage($compare) {
291 return 'Maximum call count for [' . $this->_method
.
292 '] was [' . $this->_count
.
293 '] got [' . $compare . ']';
298 * Retrieves method actions by searching the
299 * parameter lists until an expected match is found.
300 * @package SimpleTest
301 * @subpackage MockObjects
303 class SimpleSignatureMap
{
307 * Creates an empty call map.
310 function SimpleSignatureMap() {
311 $this->_map
= array();
315 * Stashes a reference against a method call.
316 * @param array $parameters Array of arguments (including wildcards).
317 * @param mixed $action Reference placed in the map.
320 function add($parameters, &$action) {
321 $place = count($this->_map
);
322 $this->_map
[$place] = array();
323 $this->_map
[$place]['params'] = new ParametersExpectation($parameters);
324 $this->_map
[$place]['content'] = &$action;
328 * Searches the call list for a matching parameter
329 * set. Returned by reference.
330 * @param array $parameters Parameters to search by
332 * @return object Object held in the first matching
333 * slot, otherwise null.
336 function &findFirstAction($parameters) {
337 $slot = $this->_findFirstSlot($parameters);
338 if (isset($slot) && isset($slot['content'])) {
339 return $slot['content'];
346 * Searches the call list for a matching parameter
347 * set. True if successful.
348 * @param array $parameters Parameters to search by
350 * @return boolean True if a match is present.
353 function isMatch($parameters) {
354 return ($this->_findFirstSlot($parameters) != null);
358 * Compares the incoming parameters with the
359 * internal expectation. Uses the incoming $test
360 * to dispatch the test message.
361 * @param SimpleTestCase $test Test to dispatch to.
362 * @param array $parameters The actual calling arguments.
363 * @param string $message The message to overlay.
366 function test(&$test, $parameters, $message) {
370 * Searches the map for a matching item.
371 * @param array $parameters Parameters to search by
373 * @return array Reference to slot or null.
376 function &_findFirstSlot($parameters) {
377 $count = count($this->_map
);
378 for ($i = 0; $i < $count; $i++
) {
379 if ($this->_map
[$i]["params"]->test($parameters)) {
380 return $this->_map
[$i];
389 * Allows setting of actions against call signatures either
390 * at a specific time, or always. Specific time settings
391 * trump lasting ones, otherwise the most recently added
392 * will mask an earlier match.
393 * @package SimpleTest
394 * @subpackage MockObjects
396 class SimpleCallSchedule
{
397 var $_wildcard = MOCK_ANYTHING
;
402 * Sets up an empty response schedule.
403 * Creates an empty call map.
405 function SimpleCallSchedule() {
406 $this->_always
= array();
407 $this->_at
= array();
411 * Stores an action against a signature that
412 * will always fire unless masked by a time
414 * @param string $method Method name.
415 * @param array $args Calling parameters.
416 * @param SimpleAction $action Actually simpleByValue, etc.
419 function register($method, $args, &$action) {
420 $args = $this->_replaceWildcards($args);
421 $method = strtolower($method);
422 if (! isset($this->_always
[$method])) {
423 $this->_always
[$method] = new SimpleSignatureMap();
425 $this->_always
[$method]->add($args, $action);
429 * Stores an action against a signature that
430 * will fire at a specific time in the future.
431 * @param integer $step delay of calls to this method,
433 * @param string $method Method name.
434 * @param array $args Calling parameters.
435 * @param SimpleAction $action Actually SimpleByValue, etc.
438 function registerAt($step, $method, $args, &$action) {
439 $args = $this->_replaceWildcards($args);
440 $method = strtolower($method);
441 if (! isset($this->_at
[$method])) {
442 $this->_at
[$method] = array();
444 if (! isset($this->_at
[$method][$step])) {
445 $this->_at
[$method][$step] = new SimpleSignatureMap();
447 $this->_at
[$method][$step]->add($args, $action);
450 function expectArguments($method, $args, $message) {
451 $args = $this->_replaceWildcards($args);
452 $message .= Mock
::getExpectationLine();
453 $this->_expected_args
[strtolower($method)] =
454 new ParametersExpectation($args, $message);
459 * Actually carry out the action stored previously,
460 * if the parameters match.
461 * @param integer $step Time of call.
462 * @param string $method Method name.
463 * @param array $args The parameters making up the
465 * @return mixed The result of the action.
468 function &respond($step, $method, $args) {
469 $method = strtolower($method);
470 if (isset($this->_at
[$method][$step])) {
471 if ($this->_at
[$method][$step]->isMatch($args)) {
472 $action = &$this->_at
[$method][$step]->findFirstAction($args);
473 if (isset($action)) {
474 return $action->act();
478 if (isset($this->_always
[$method])) {
479 $action = &$this->_always
[$method]->findFirstAction($args);
480 if (isset($action)) {
481 return $action->act();
489 * Replaces wildcard matches with wildcard
490 * expectations in the argument list.
491 * @param array $args Raw argument list.
492 * @return array Argument list with
496 function _replaceWildcards($args) {
497 if ($args === false) {
500 for ($i = 0; $i < count($args); $i++
) {
501 if ($args[$i] === $this->_wildcard
) {
502 $args[$i] = new AnythingExpectation();
510 * A type of SimpleMethodAction.
511 * Stashes a reference for returning later.
512 * @package SimpleTest
513 * @subpackage MockObjects
515 class SimpleByReference
{
519 * Stashes it for later.
520 * @param mixed $reference Actual PHP4 style reference.
523 function SimpleByReference(&$reference) {
524 $this->_reference
= &$reference;
528 * Returns the reference stored earlier.
529 * @return mixed Whatever was stashed.
533 return $this->_reference
;
538 * A type of SimpleMethodAction.
539 * Stashes a value for returning later.
540 * @package SimpleTest
541 * @subpackage MockObjects
543 class SimpleByValue
{
547 * Stashes it for later.
548 * @param mixed $value You need to clone objects
549 * if you want copy semantics
553 function SimpleByValue($value) {
554 $this->_value
= $value;
558 * Returns the value stored earlier.
559 * @return mixed Whatever was stashed.
563 $dummy = $this->_value
;
569 * A type of SimpleMethodAction.
570 * Stashes an exception for throwing later.
571 * @package SimpleTest
572 * @subpackage MockObjects
574 class SimpleThrower
{
578 * Stashes it for later.
579 * @param Exception $exception The exception object to throw.
582 function SimpleThrower($exception) {
583 $this->_exception
= $exception;
587 * Throws the exceptins stashed earlier.
591 eval('throw $this->_exception;');
596 * A type of SimpleMethodAction.
597 * Stashes an error for emitting later.
598 * @package SimpleTest
599 * @subpackage MockObjects
601 class SimpleErrorThrower
{
606 * Stashes an error to throw later.
607 * @param string $error Error message.
608 * @param integer $severity PHP error constant, e.g E_USER_ERROR.
611 function SimpleErrorThrower($error, $severity) {
612 $this->_error
= $error;
613 $this->_severity
= $severity;
617 * Triggers the stashed error.
618 * @return null The usual PHP4.4 shenanigans are needed here.
622 trigger_error($this->_error
, $this->_severity
);
629 * A base class or delegate that extends an
630 * empty collection of methods that can have their
631 * return values set and expectations made of the
632 * calls upon them. The mock will assert the
633 * expectations against it's attached test case in
634 * addition to the server stub behaviour or returning
635 * preprogrammed responses.
636 * @package SimpleTest
637 * @subpackage MockObjects
641 var $_wildcard = MOCK_ANYTHING
;
642 var $_is_strict = true;
644 var $_expected_counts;
647 var $_expected_args_at;
650 * Creates an empty action list and expectation list.
651 * All call counts are set to zero.
654 function SimpleMock() {
655 $this->_actions
= new SimpleCallSchedule();
656 $this->_expectations
= new SimpleCallSchedule();
657 $this->_call_counts
= array();
658 $this->_expected_counts
= array();
659 $this->_max_counts
= array();
660 $this->_expected_args
= array();
661 $this->_expected_args_at
= array();
662 $test = &$this->_getCurrentTestCase();
667 * Disables a name check when setting expectations.
668 * This hack is needed for the partial mocks.
671 function disableExpectationNameChecks() {
672 $this->_is_strict
= false;
676 * Finds currently running test.
677 * @return SimpeTestCase Current test case.
680 function &_getCurrentTestCase() {
681 $context = &SimpleTest
::getContext();
682 return $context->getTest();
686 * Die if bad arguments array is passed.
687 * @param mixed $args The arguments value to be checked.
688 * @param string $task Description of task attempt.
689 * @return boolean Valid arguments
692 function _checkArgumentsIsArray($args, $task) {
693 if (! is_array($args)) {
695 "Cannot $task as \$args parameter is not an array",
701 * Triggers a PHP error if the method is not part
703 * @param string $method Name of method.
704 * @param string $task Description of task attempt.
707 function _dieOnNoMethod($method, $task) {
708 if ($this->_is_strict
&& ! method_exists($this, $method)) {
710 "Cannot $task as no ${method}() in class " . get_class($this),
716 * Replaces wildcard matches with wildcard
717 * expectations in the argument list.
718 * @param array $args Raw argument list.
719 * @return array Argument list with
723 function _replaceWildcards($args) {
724 if ($args === false) {
727 for ($i = 0; $i < count($args); $i++
) {
728 if ($args[$i] === $this->_wildcard
) {
729 $args[$i] = new AnythingExpectation();
736 * Adds one to the call count of a method.
737 * @param string $method Method called.
738 * @param array $args Arguments as an array.
741 function _addCall($method, $args) {
742 if (! isset($this->_call_counts
[$method])) {
743 $this->_call_counts
[$method] = 0;
745 $this->_call_counts
[$method]++
;
749 * Fetches the call count of a method so far.
750 * @param string $method Method name called.
751 * @return integer Number of calls so far.
754 function getCallCount($method) {
755 $this->_dieOnNoMethod($method, "get call count");
756 $method = strtolower($method);
757 if (! isset($this->_call_counts
[$method])) {
760 return $this->_call_counts
[$method];
764 * Sets a return for a parameter list that will
765 * be passed by value for all calls to this method.
766 * @param string $method Method name.
767 * @param mixed $value Result of call passed by value.
768 * @param array $args List of parameters to match
769 * including wildcards.
772 function setReturnValue($method, $value, $args = false) {
773 $this->_dieOnNoMethod($method, "set return value");
774 $this->_actions
->register($method, $args, new SimpleByValue($value));
778 * Sets a return for a parameter list that will
779 * be passed by value only when the required call count
781 * @param integer $timing Number of calls in the future
782 * to which the result applies. If
783 * not set then all calls will return
785 * @param string $method Method name.
786 * @param mixed $value Result of call passed by value.
787 * @param array $args List of parameters to match
788 * including wildcards.
791 function setReturnValueAt($timing, $method, $value, $args = false) {
792 $this->_dieOnNoMethod($method, "set return value sequence");
793 $this->_actions
->registerAt($timing, $method, $args, new SimpleByValue($value));
797 * Sets a return for a parameter list that will
798 * be passed by reference for all calls.
799 * @param string $method Method name.
800 * @param mixed $reference Result of the call will be this object.
801 * @param array $args List of parameters to match
802 * including wildcards.
805 function setReturnReference($method, &$reference, $args = false) {
806 $this->_dieOnNoMethod($method, "set return reference");
807 $this->_actions
->register($method, $args, new SimpleByReference($reference));
811 * Sets a return for a parameter list that will
812 * be passed by value only when the required call count
814 * @param integer $timing Number of calls in the future
815 * to which the result applies. If
816 * not set then all calls will return
818 * @param string $method Method name.
819 * @param mixed $reference Result of the call will be this object.
820 * @param array $args List of parameters to match
821 * including wildcards.
824 function setReturnReferenceAt($timing, $method, &$reference, $args = false) {
825 $this->_dieOnNoMethod($method, "set return reference sequence");
826 $this->_actions
->registerAt($timing, $method, $args, new SimpleByReference($reference));
830 * Sets up an expected call with a set of
831 * expected parameters in that call. All
832 * calls will be compared to these expectations
833 * regardless of when the call is made.
834 * @param string $method Method call to test.
835 * @param array $args Expected parameters for the call
836 * including wildcards.
837 * @param string $message Overridden message.
840 function expect($method, $args, $message = '%s') {
841 $this->_dieOnNoMethod($method, 'set expected arguments');
842 $this->_checkArgumentsIsArray($args, 'set expected arguments');
843 $this->_expectations
->expectArguments($method, $args, $message);
844 $args = $this->_replaceWildcards($args);
845 $message .= Mock
::getExpectationLine();
846 $this->_expected_args
[strtolower($method)] =
847 new ParametersExpectation($args, $message);
853 function expectArguments($method, $args, $message = '%s') {
854 return $this->expect($method, $args, $message);
858 * Sets up an expected call with a set of
859 * expected parameters in that call. The
860 * expected call count will be adjusted if it
861 * is set too low to reach this call.
862 * @param integer $timing Number of calls in the future at
863 * which to test. Next call is 0.
864 * @param string $method Method call to test.
865 * @param array $args Expected parameters for the call
866 * including wildcards.
867 * @param string $message Overridden message.
870 function expectAt($timing, $method, $args, $message = '%s') {
871 $this->_dieOnNoMethod($method, 'set expected arguments at time');
872 $this->_checkArgumentsIsArray($args, 'set expected arguments at time');
873 $args = $this->_replaceWildcards($args);
874 if (! isset($this->_expected_args_at
[$timing])) {
875 $this->_expected_args_at
[$timing] = array();
877 $method = strtolower($method);
878 $message .= Mock
::getExpectationLine();
879 $this->_expected_args_at
[$timing][$method] =
880 new ParametersExpectation($args, $message);
886 function expectArgumentsAt($timing, $method, $args, $message = '%s') {
887 return $this->expectAt($timing, $method, $args, $message);
891 * Sets an expectation for the number of times
892 * a method will be called. The tally method
893 * is used to check this.
894 * @param string $method Method call to test.
895 * @param integer $count Number of times it should
896 * have been called at tally.
897 * @param string $message Overridden message.
900 function expectCallCount($method, $count, $message = '%s') {
901 $this->_dieOnNoMethod($method, 'set expected call count');
902 $message .= Mock
::getExpectationLine();
903 $this->_expected_counts
[strtolower($method)] =
904 new CallCountExpectation($method, $count, $message);
908 * Sets the number of times a method may be called
909 * before a test failure is triggered.
910 * @param string $method Method call to test.
911 * @param integer $count Most number of times it should
913 * @param string $message Overridden message.
916 function expectMaximumCallCount($method, $count, $message = '%s') {
917 $this->_dieOnNoMethod($method, 'set maximum call count');
918 $message .= Mock
::getExpectationLine();
919 $this->_max_counts
[strtolower($method)] =
920 new MaximumCallCountExpectation($method, $count, $message);
924 * Sets the number of times to call a method to prevent
925 * a failure on the tally.
926 * @param string $method Method call to test.
927 * @param integer $count Least number of times it should
929 * @param string $message Overridden message.
932 function expectMinimumCallCount($method, $count, $message = '%s') {
933 $this->_dieOnNoMethod($method, 'set minimum call count');
934 $message .= Mock
::getExpectationLine();
935 $this->_expected_counts
[strtolower($method)] =
936 new MinimumCallCountExpectation($method, $count, $message);
940 * Convenience method for barring a method
942 * @param string $method Method call to ban.
943 * @param string $message Overridden message.
946 function expectNever($method, $message = '%s') {
947 $this->expectMaximumCallCount($method, 0, $message);
951 * Convenience method for a single method
953 * @param string $method Method call to track.
954 * @param array $args Expected argument list or
955 * false for any arguments.
956 * @param string $message Overridden message.
959 function expectOnce($method, $args = false, $message = '%s') {
960 $this->expectCallCount($method, 1, $message);
961 if ($args !== false) {
962 $this->expect($method, $args, $message);
967 * Convenience method for requiring a method
969 * @param string $method Method call to track.
970 * @param array $args Expected argument list or
971 * false for any arguments.
972 * @param string $message Overridden message.
975 function expectAtLeastOnce($method, $args = false, $message = '%s') {
976 $this->expectMinimumCallCount($method, 1, $message);
977 if ($args !== false) {
978 $this->expect($method, $args, $message);
983 * Sets up a trigger to throw an exception upon the
985 * @param string $method Method name to throw on.
987 function throwOn($method, $exception = false, $args = false) {
988 $this->_dieOnNoMethod($method, "throw on");
989 $this->_actions
->register($method, $args,
990 new SimpleThrower($exception ?
$exception : new Exception()));
994 * Sets up a trigger to throw an exception upon the
997 function throwAt($timing, $method, $exception = false, $args = false) {
998 $this->_dieOnNoMethod($method, "throw at");
999 $this->_actions
->registerAt($timing, $method, $args,
1000 new SimpleThrower($exception ?
$exception : new Exception()));
1004 * Sets up a trigger to throw an error upon the
1007 function errorOn($method, $error = 'A mock error', $args = false, $severity = E_USER_ERROR
) {
1008 $this->_dieOnNoMethod($method, "error on");
1009 $this->_actions
->register($method, $args, new SimpleErrorThrower($error, $severity));
1013 * Sets up a trigger to throw an error upon the
1016 function errorAt($timing, $method, $error = 'A mock error', $args = false, $severity = E_USER_ERROR
) {
1017 $this->_dieOnNoMethod($method, "error at");
1018 $this->_actions
->registerAt($timing, $method, $args, new SimpleErrorThrower($error, $severity));
1028 * Receives event from unit test that the current
1029 * test method has finished. Totals up the call
1030 * counts and triggers a test assertion if a test
1031 * is present for expected call counts.
1032 * @param string $test_method Current method name.
1033 * @param SimpleTestCase $test Test to send message to.
1036 function atTestEnd($test_method, &$test) {
1037 foreach ($this->_expected_counts
as $method => $expectation) {
1038 $test->assert($expectation, $this->getCallCount($method));
1040 foreach ($this->_max_counts
as $method => $expectation) {
1041 if ($expectation->test($this->getCallCount($method))) {
1042 $test->assert($expectation, $this->getCallCount($method));
1048 * Returns the expected value for the method name
1049 * and checks expectations. Will generate any
1050 * test assertions as a result of expectations
1051 * if there is a test present.
1052 * @param string $method Name of method to simulate.
1053 * @param array $args Arguments as an array.
1054 * @return mixed Stored return.
1057 function &_invoke($method, $args) {
1058 $method = strtolower($method);
1059 $step = $this->getCallCount($method);
1060 $this->_addCall($method, $args);
1061 $this->_checkExpectations($method, $args, $step);
1062 $result = &$this->_emulateCall($method, $args, $step);
1067 * Finds the return value matching the incoming
1068 * arguments. If there is no matching value found
1069 * then an error is triggered.
1070 * @param string $method Method name.
1071 * @param array $args Calling arguments.
1072 * @param integer $step Current position in the
1074 * @return mixed Stored return or other action.
1077 function &_emulateCall($method, $args, $step) {
1078 return $this->_actions
->respond($step, $method, $args);
1082 * Tests the arguments against expectations.
1083 * @param string $method Method to check.
1084 * @param array $args Argument list to match.
1085 * @param integer $timing The position of this call
1086 * in the call history.
1089 function _checkExpectations($method, $args, $timing) {
1090 $test = &$this->_getCurrentTestCase();
1091 if (isset($this->_max_counts
[$method])) {
1092 if (! $this->_max_counts
[$method]->test($timing +
1)) {
1093 $test->assert($this->_max_counts
[$method], $timing +
1);
1096 if (isset($this->_expected_args_at
[$timing][$method])) {
1098 $this->_expected_args_at
[$timing][$method],
1100 "Mock method [$method] at [$timing] -> %s");
1101 } elseif (isset($this->_expected_args
[$method])) {
1103 $this->_expected_args
[$method],
1105 "Mock method [$method] -> %s");
1111 * Static methods only service class for code generation of
1113 * @package SimpleTest
1114 * @subpackage MockObjects
1119 * Factory for mock object classes.
1123 trigger_error('Mock factory methods are static.');
1127 * Clones a class' interface and creates a mock version
1128 * that can have return values and expectations set.
1129 * @param string $class Class to clone.
1130 * @param string $mock_class New class name. Default is
1131 * the old name with "Mock"
1133 * @param array $methods Additional methods to add beyond
1134 * those in the cloned class. Use this
1135 * to emulate the dynamic addition of
1136 * methods in the cloned class or when
1137 * the class hasn't been written yet.
1141 function generate($class, $mock_class = false, $methods = false) {
1142 $generator = new MockGenerator($class, $mock_class);
1143 return $generator->generateSubclass($methods);
1147 * Generates a version of a class with selected
1148 * methods mocked only. Inherits the old class
1149 * and chains the mock methods of an aggregated
1151 * @param string $class Class to clone.
1152 * @param string $mock_class New class name.
1153 * @param array $methods Methods to be overridden
1154 * with mock versions.
1158 function generatePartial($class, $mock_class, $methods) {
1159 $generator = new MockGenerator($class, $mock_class);
1160 return $generator->generatePartial($methods);
1164 * Uses a stack trace to find the line of an assertion.
1168 function getExpectationLine() {
1169 $trace = new SimpleStackTrace(array('expect'));
1170 return $trace->traceMethod();
1175 * @package SimpleTest
1176 * @subpackage MockObjects
1179 class Stub
extends Mock
{
1183 * Service class for code generation of mock objects.
1184 * @package SimpleTest
1185 * @subpackage MockObjects
1187 class MockGenerator
{
1194 * Builds initial reflection object.
1195 * @param string $class Class to be mocked.
1196 * @param string $mock_class New class with identical interface,
1199 function MockGenerator($class, $mock_class) {
1200 $this->_class
= $class;
1201 $this->_mock_class
= $mock_class;
1202 if (! $this->_mock_class
) {
1203 $this->_mock_class
= 'Mock' . $this->_class
;
1205 $this->_mock_base
= SimpleTest
::getMockBaseClass();
1206 $this->_reflection
= new SimpleReflection($this->_class
);
1210 * Clones a class' interface and creates a mock version
1211 * that can have return values and expectations set.
1212 * @param array $methods Additional methods to add beyond
1213 * those in th cloned class. Use this
1214 * to emulate the dynamic addition of
1215 * methods in the cloned class or when
1216 * the class hasn't been written yet.
1219 function generate($methods) {
1220 if (! $this->_reflection
->classOrInterfaceExists()) {
1223 $mock_reflection = new SimpleReflection($this->_mock_class
);
1224 if ($mock_reflection->classExistsSansAutoload()) {
1227 $code = $this->_createClassCode($methods ?
$methods : array());
1228 return eval("$code return \$code;");
1232 * Subclasses a class and overrides every method with a mock one
1233 * that can have return values and expectations set. Chains
1234 * to an aggregated SimpleMock.
1235 * @param array $methods Additional methods to add beyond
1236 * those in the cloned class. Use this
1237 * to emulate the dynamic addition of
1238 * methods in the cloned class or when
1239 * the class hasn't been written yet.
1242 function generateSubclass($methods) {
1243 if (! $this->_reflection
->classOrInterfaceExists()) {
1246 $mock_reflection = new SimpleReflection($this->_mock_class
);
1247 if ($mock_reflection->classExistsSansAutoload()) {
1250 if ($this->_reflection
->isInterface() ||
$this->_reflection
->hasFinal()) {
1251 $code = $this->_createClassCode($methods ?
$methods : array());
1252 return eval("$code return \$code;");
1254 $code = $this->_createSubclassCode($methods ?
$methods : array());
1255 return eval("$code return \$code;");
1260 * Generates a version of a class with selected
1261 * methods mocked only. Inherits the old class
1262 * and chains the mock methods of an aggregated
1264 * @param array $methods Methods to be overridden
1265 * with mock versions.
1268 function generatePartial($methods) {
1269 if (! $this->_reflection
->classExists($this->_class
)) {
1272 $mock_reflection = new SimpleReflection($this->_mock_class
);
1273 if ($mock_reflection->classExistsSansAutoload()) {
1274 trigger_error('Partial mock class [' . $this->_mock_class
. '] already exists');
1277 $code = $this->_extendClassCode($methods);
1278 return eval("$code return \$code;");
1282 * The new mock class code as a string.
1283 * @param array $methods Additional methods.
1284 * @return string Code for new mock class.
1287 function _createClassCode($methods) {
1289 $interfaces = $this->_reflection
->getInterfaces();
1290 if (function_exists('spl_classes')) {
1291 $interfaces = array_diff($interfaces, array('Traversable'));
1293 if (count($interfaces) > 0) {
1294 $implements = 'implements ' . implode(', ', $interfaces);
1296 $code = "class " . $this->_mock_class
. " extends " . $this->_mock_base
. " $implements {\n";
1297 $code .= " function " . $this->_mock_class
. "() {\n";
1298 $code .= " \$this->" . $this->_mock_base
. "();\n";
1300 if (in_array('__construct', $this->_reflection
->getMethods())) {
1301 $code .= " " . $this->_reflection
->getSignature('__construct') . " {\n";
1302 $code .= " \$this->" . $this->_mock_base
. "();\n";
1305 $code .= $this->_createHandlerCode($methods);
1311 * The new mock class code as a string. The mock will
1312 * be a subclass of the original mocked class.
1313 * @param array $methods Additional methods.
1314 * @return string Code for new mock class.
1317 function _createSubclassCode($methods) {
1318 $code = "class " . $this->_mock_class
. " extends " . $this->_class
. " {\n";
1319 $code .= " var \$_mock;\n";
1320 $code .= $this->_addMethodList(array_merge($methods, $this->_reflection
->getMethods()));
1322 $code .= " function " . $this->_mock_class
. "() {\n";
1323 $code .= " \$this->_mock = new " . $this->_mock_base
. "();\n";
1324 $code .= " \$this->_mock->disableExpectationNameChecks();\n";
1326 $code .= $this->_chainMockReturns();
1327 $code .= $this->_chainMockExpectations();
1328 $code .= $this->_chainThrowMethods();
1329 $code .= $this->_overrideMethods($this->_reflection
->getMethods());
1330 $code .= $this->_createNewMethodCode($methods);
1336 * The extension class code as a string. The class
1337 * composites a mock object and chains mocked methods
1339 * @param array $methods Mocked methods.
1340 * @return string Code for a new class.
1343 function _extendClassCode($methods) {
1344 $code = "class " . $this->_mock_class
. " extends " . $this->_class
. " {\n";
1345 $code .= " var \$_mock;\n";
1346 $code .= $this->_addMethodList($methods);
1348 $code .= " function " . $this->_mock_class
. "() {\n";
1349 $code .= " \$this->_mock = new " . $this->_mock_base
. "();\n";
1350 $code .= " \$this->_mock->disableExpectationNameChecks();\n";
1352 $code .= $this->_chainMockReturns();
1353 $code .= $this->_chainMockExpectations();
1354 $code .= $this->_chainThrowMethods();
1355 $code .= $this->_overrideMethods($methods);
1361 * Creates code within a class to generate replaced
1362 * methods. All methods call the _invoke() handler
1363 * with the method name and the arguments in an
1365 * @param array $methods Additional methods.
1368 function _createHandlerCode($methods) {
1370 $methods = array_merge($methods, $this->_reflection
->getMethods());
1371 foreach ($methods as $method) {
1372 if ($this->_isConstructor($method)) {
1375 $mock_reflection = new SimpleReflection($this->_mock_base
);
1376 if (in_array($method, $mock_reflection->getMethods())) {
1379 $code .= " " . $this->_reflection
->getSignature($method) . " {\n";
1380 $code .= " \$args = func_get_args();\n";
1381 $code .= " \$result = &\$this->_invoke(\"$method\", \$args);\n";
1382 $code .= " return \$result;\n";
1389 * Creates code within a class to generate a new
1390 * methods. All methods call the _invoke() handler
1391 * on the internal mock with the method name and
1392 * the arguments in an array.
1393 * @param array $methods Additional methods.
1396 function _createNewMethodCode($methods) {
1398 foreach ($methods as $method) {
1399 if ($this->_isConstructor($method)) {
1402 $mock_reflection = new SimpleReflection($this->_mock_base
);
1403 if (in_array($method, $mock_reflection->getMethods())) {
1406 $code .= " " . $this->_reflection
->getSignature($method) . " {\n";
1407 $code .= " \$args = func_get_args();\n";
1408 $code .= " \$result = &\$this->_mock->_invoke(\"$method\", \$args);\n";
1409 $code .= " return \$result;\n";
1416 * Tests to see if a special PHP method is about to
1417 * be stubbed by mistake.
1418 * @param string $method Method name.
1419 * @return boolean True if special.
1422 function _isConstructor($method) {
1424 strtolower($method),
1425 array('__construct', '__destruct'));
1429 * Creates a list of mocked methods for error checking.
1430 * @param array $methods Mocked methods.
1431 * @return string Code for a method list.
1434 function _addMethodList($methods) {
1435 return " var \$_mocked_methods = array('" .
1436 implode("', '", array_map('strtolower', $methods)) .
1441 * Creates code to abandon the expectation if not mocked.
1442 * @param string $alias Parameter name of method name.
1443 * @return string Code for bail out.
1446 function _bailOutIfNotMocked($alias) {
1447 $code = " if (! in_array(strtolower($alias), \$this->_mocked_methods)) {\n";
1448 $code .= " trigger_error(\"Method [$alias] is not mocked\");\n";
1449 $code .= " \$null = null;\n";
1450 $code .= " return \$null;\n";
1456 * Creates source code for chaining to the composited
1458 * @return string Code for mock set up.
1461 function _chainMockReturns() {
1462 $code = " function setReturnValue(\$method, \$value, \$args = false) {\n";
1463 $code .= $this->_bailOutIfNotMocked("\$method");
1464 $code .= " \$this->_mock->setReturnValue(\$method, \$value, \$args);\n";
1466 $code .= " function setReturnValueAt(\$timing, \$method, \$value, \$args = false) {\n";
1467 $code .= $this->_bailOutIfNotMocked("\$method");
1468 $code .= " \$this->_mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n";
1470 $code .= " function setReturnReference(\$method, &\$ref, \$args = false) {\n";
1471 $code .= $this->_bailOutIfNotMocked("\$method");
1472 $code .= " \$this->_mock->setReturnReference(\$method, \$ref, \$args);\n";
1474 $code .= " function setReturnReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n";
1475 $code .= $this->_bailOutIfNotMocked("\$method");
1476 $code .= " \$this->_mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n";
1482 * Creates source code for chaining to an aggregated
1484 * @return string Code for expectations.
1487 function _chainMockExpectations() {
1488 $code = " function expect(\$method, \$args = false, \$msg = '%s') {\n";
1489 $code .= $this->_bailOutIfNotMocked("\$method");
1490 $code .= " \$this->_mock->expect(\$method, \$args, \$msg);\n";
1492 $code .= " function expectArguments(\$method, \$args = false, \$msg = '%s') {\n";
1493 $code .= $this->_bailOutIfNotMocked("\$method");
1494 $code .= " \$this->_mock->expectArguments(\$method, \$args, \$msg);\n";
1496 $code .= " function expectAt(\$timing, \$method, \$args = false, \$msg = '%s') {\n";
1497 $code .= $this->_bailOutIfNotMocked("\$method");
1498 $code .= " \$this->_mock->expectArgumentsAt(\$timing, \$method, \$args, \$msg);\n";
1500 $code .= " function expectArgumentsAt(\$timing, \$method, \$args = false, \$msg = '%s') {\n";
1501 $code .= $this->_bailOutIfNotMocked("\$method");
1502 $code .= " \$this->_mock->expectArgumentsAt(\$timing, \$method, \$args, \$msg);\n";
1504 $code .= " function expectCallCount(\$method, \$count) {\n";
1505 $code .= $this->_bailOutIfNotMocked("\$method");
1506 $code .= " \$this->_mock->expectCallCount(\$method, \$count, \$msg = '%s');\n";
1508 $code .= " function expectMaximumCallCount(\$method, \$count, \$msg = '%s') {\n";
1509 $code .= $this->_bailOutIfNotMocked("\$method");
1510 $code .= " \$this->_mock->expectMaximumCallCount(\$method, \$count, \$msg = '%s');\n";
1512 $code .= " function expectMinimumCallCount(\$method, \$count, \$msg = '%s') {\n";
1513 $code .= $this->_bailOutIfNotMocked("\$method");
1514 $code .= " \$this->_mock->expectMinimumCallCount(\$method, \$count, \$msg = '%s');\n";
1516 $code .= " function expectNever(\$method) {\n";
1517 $code .= $this->_bailOutIfNotMocked("\$method");
1518 $code .= " \$this->_mock->expectNever(\$method);\n";
1520 $code .= " function expectOnce(\$method, \$args = false, \$msg = '%s') {\n";
1521 $code .= $this->_bailOutIfNotMocked("\$method");
1522 $code .= " \$this->_mock->expectOnce(\$method, \$args, \$msg);\n";
1524 $code .= " function expectAtLeastOnce(\$method, \$args = false, \$msg = '%s') {\n";
1525 $code .= $this->_bailOutIfNotMocked("\$method");
1526 $code .= " \$this->_mock->expectAtLeastOnce(\$method, \$args, \$msg);\n";
1528 $code .= " function tally() {\n";
1534 * Adds code for chaining the throw methods.
1535 * @return string Code for chains.
1538 function _chainThrowMethods() {
1539 $code = " function throwOn(\$method, \$exception = false, \$args = false) {\n";
1540 $code .= $this->_bailOutIfNotMocked("\$method");
1541 $code .= " \$this->_mock->throwOn(\$method, \$exception, \$args);\n";
1543 $code .= " function throwAt(\$timing, \$method, \$exception = false, \$args = false) {\n";
1544 $code .= $this->_bailOutIfNotMocked("\$method");
1545 $code .= " \$this->_mock->throwAt(\$timing, \$method, \$exception, \$args);\n";
1547 $code .= " function errorOn(\$method, \$error = 'A mock error', \$args = false, \$severity = E_USER_ERROR) {\n";
1548 $code .= $this->_bailOutIfNotMocked("\$method");
1549 $code .= " \$this->_mock->errorOn(\$method, \$error, \$args, \$severity);\n";
1551 $code .= " function errorAt(\$timing, \$method, \$error = 'A mock error', \$args = false, \$severity = E_USER_ERROR) {\n";
1552 $code .= $this->_bailOutIfNotMocked("\$method");
1553 $code .= " \$this->_mock->errorAt(\$timing, \$method, \$error, \$args, \$severity);\n";
1559 * Creates source code to override a list of methods
1560 * with mock versions.
1561 * @param array $methods Methods to be overridden
1562 * with mock versions.
1563 * @return string Code for overridden chains.
1566 function _overrideMethods($methods) {
1568 foreach ($methods as $method) {
1569 if ($this->_isConstructor($method)) {
1572 $code .= " " . $this->_reflection
->getSignature($method) . " {\n";
1573 $code .= " \$args = func_get_args();\n";
1574 $code .= " \$result = &\$this->_mock->_invoke(\"$method\", \$args);\n";
1575 $code .= " return \$result;\n";