From 16533b01d73cc739f41948395425caefd42dea82 Mon Sep 17 00:00:00 2001 From: tmont Date: Wed, 17 Jun 2009 06:09:53 +0000 Subject: [PATCH] MOCK OBJECTS FOR LIFE --- .../framework/mock/InvocationExpectation.php | 97 +++++++++++++++++++ .../framework/mock/InvocationTracker.php | 12 +++ src/TUnit/framework/mock/MockHandler.php | 19 ++++ src/TUnit/framework/mock/MockInvocation.php | 18 +++- src/TUnit/framework/mock/MockObject.php | 73 +++++++++++--- src/TUnit/framework/test/TestCase.php | 11 ++- src/TUnit/manifest.php | 4 +- 7 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 src/TUnit/framework/mock/InvocationExpectation.php create mode 100644 src/TUnit/framework/mock/MockHandler.php diff --git a/src/TUnit/framework/mock/InvocationExpectation.php b/src/TUnit/framework/mock/InvocationExpectation.php new file mode 100644 index 0000000..1712aed --- /dev/null +++ b/src/TUnit/framework/mock/InvocationExpectation.php @@ -0,0 +1,97 @@ +method = $methodName; + $this->count = 0; + $this->args = array(); + $this->returnValue = null; + $this->echoString = null; + } + + public final function getMethod() { + return $this->method; + } + + public final function getCount() { + return $this->count; + } + + public final function getArgs() { + return $this->args; + } + + public function toBeCalled($count) { + $this->count = $count; + return $this; + } + + public function toBeCallExactly($count) { + throw new BadMethodCallException('Not implemented yet'); + } + + public function withArguments() { + $this->args = func_get_args(); + return $this; + } + + public function toEcho($value) { + if (!is_scalar($value)) { + if (is_object($value)) { + $refClass = new ReflectionClass($value); + if ($refClass->hasMethod('__toString')) { + $value = $refClass->getMethod('__toString')->invoke($value); + } else { + throw new InvalidArgumentException('1st argument must be a scalar value or a __toString()-able object'); + } + } else { + throw new InvalidArgumentException('1st argument must be a scalar value or a __toString()-able object'); + } + } + + $this->echoString = $value; + return $this; + } + + public function toReturn($value) { + $this->returnValue = $value; + } + + public function andReturn($value) { + $this->toReturn($value); + } + + public function execute() { + if (!empty($this->echoString)) { + echo $this->echoString; + } + + return $this->returnValue; + } + + public function matchesInvocation(MockInvocation $invocation) { + return $this->method === $invocation->getMethod() && $this->countIsAcceptable($invocation->getCount()) && $this->args == $invocation->getArgs(); + } + + protected function countIsAcceptable($count) { + switch ($count) { + case TestCase::ANY: + return true; + case TestCase::AT_LEAST_ONCE: + return $count > 0; + default: + return $count === $this->count; + } + } + + } + +?> \ No newline at end of file diff --git a/src/TUnit/framework/mock/InvocationTracker.php b/src/TUnit/framework/mock/InvocationTracker.php index a4e7b7f..ed3a6da 100644 --- a/src/TUnit/framework/mock/InvocationTracker.php +++ b/src/TUnit/framework/mock/InvocationTracker.php @@ -10,6 +10,18 @@ public function registerInvocation(MockInvocation $invocation) { $this->invocations[] = $invocation; + + foreach (MockObjectCreator::getExpectations($invocation->getClass()) as $expectation) { + if ($expectation->matchesInvocation($invocation)) { + return $expectation; + } + } + + return false; + } + + public function getInvocations() { + return $this->invocations; } } diff --git a/src/TUnit/framework/mock/MockHandler.php b/src/TUnit/framework/mock/MockHandler.php new file mode 100644 index 0000000..af55b8f --- /dev/null +++ b/src/TUnit/framework/mock/MockHandler.php @@ -0,0 +1,19 @@ +className = get_class($mock); + } + + public function expectsMethod($methodName) { + $expectation = new InvocationExpectation($methodName); + MockObjectCreator::addExpectation($this->className, $expectation); + return $expectation; + } + + } + +?> \ No newline at end of file diff --git a/src/TUnit/framework/mock/MockInvocation.php b/src/TUnit/framework/mock/MockInvocation.php index b43749e..d71d6b3 100644 --- a/src/TUnit/framework/mock/MockInvocation.php +++ b/src/TUnit/framework/mock/MockInvocation.php @@ -2,12 +2,16 @@ class MockInvocation { + protected $className; protected $method; protected $args; + protected $count; - public function __construct($methodName, array $args) { - $this->method = $methodName; - $this->args = $args; + public function __construct($className, $methodName, array $args, $count) { + $this->className = $className; + $this->method = $methodName; + $this->args = $args; + $this->count = $count; } public function getMethod() { @@ -18,6 +22,14 @@ return $this->args; } + public function getClass() { + return $this->className; + } + + public function getCount() { + return $this->count; + } + } ?> \ No newline at end of file diff --git a/src/TUnit/framework/mock/MockObject.php b/src/TUnit/framework/mock/MockObject.php index d22ddda..ce2b07a 100644 --- a/src/TUnit/framework/mock/MockObject.php +++ b/src/TUnit/framework/mock/MockObject.php @@ -8,6 +8,7 @@ protected $methods; protected static $registry = array(); + protected static $expectations = array(); public function __construct($class, $callParent = true) { if (!class_exists($class) && !interface_exists($class)) { @@ -32,12 +33,35 @@ ); } + public static function resetTrackers() { + self::$registry = array(); + self::$expectations = array(); + } + + public static function addExpectation($className, InvocationExpectation $expectation) { + if (!isset(self::$expectations[$className])) { + throw new LogicException('Unable to add invocation expectation because the object does not exist in the registry'); + } + + self::$expectations[$className][] = $expectation; + } + + public static function getExpectations($className) { + if (!isset(self::$expectations[$className])) { + throw new LogicException('Unable to add invocation expectation because the object does not exist in the registry'); + } + + return self::$expectations[$className]; + } + public static function registerInvocation($className, $methodName, array $args) { if (!isset(self::$registry[$className])) { throw new LogicException('Unable to register invocation because the object does not exist in the registry'); } - self::$registry[$className]->registerInvocation(new MockInvocation($methodName, $args)); + $count = self::getInvocationCount($className, $methodName); + $expectation = self::$registry[$className]->registerInvocation(new MockInvocation($className, $methodName, $args, $count)); + return $expectation; } public static function getTracker($name) { @@ -48,6 +72,17 @@ return self::$registry[$name]; } + private static function getInvocationCount($className, $methodName) { + $count = 1; + foreach (self::$registry[$className]->getInvocations() as $invocation) { + if ($invocation->getMethod() === $methodName) { + $count++; + } + } + + return $count; + } + public function addMethod($methodName, $callParent = false, $body = '') { $methodType = 'generic'; if ($this->referenceObject->hasMethod($methodName)) { @@ -91,8 +126,10 @@ $code = $this->generateClassDefinition($name); eval($code); + //print_r($code); exit; - self::$registry[$name] = new InvocationTracker(); + self::$registry[$name] = new InvocationTracker(); + self::$expectations[$name] = array(); $obj = new ReflectionClass($name); $obj = $obj->newInstanceArgs($constructorArgs); @@ -130,21 +167,29 @@ $paramList = Util::buildParameterNameList($method); } - $varName = '$___args_' . uniqid(); + $temp1 = '$___temp1_' . uniqid(); + $temp2 = '$___temp2_' . uniqid(); + $parentCall = ($methodData['call_parent']) ? "parent::$name($paramList);" : '//placeholder for call to parent'; + $body = (!empty($methodData['body'])) ? str_replace("\n", "\n\t\t", $methodData['body']) : '//placeholder for custom method body'; + $code = <<execute(); + } + unset($temp1, $temp2); + + $parentCall + + $body + } +CODE; - $code .= " }"; return $code; } diff --git a/src/TUnit/framework/test/TestCase.php b/src/TUnit/framework/test/TestCase.php index 6dd20fe..faf5d4f 100644 --- a/src/TUnit/framework/test/TestCase.php +++ b/src/TUnit/framework/test/TestCase.php @@ -4,6 +4,11 @@ protected $name; + const ANY = -1; + const AT_LEAST_ONCE = -2; + + protected static $mockStorage = array(); + public function __construct($name) { $this->name = $name; } @@ -13,7 +18,7 @@ } public function setUp() { - + } public function tearDown() { @@ -60,6 +65,10 @@ return $creator->generate($args, $name); } + protected function mock(MockObject $mock) { + return new MockHandler($mock); + } + } ?> \ No newline at end of file diff --git a/src/TUnit/manifest.php b/src/TUnit/manifest.php index 5a52539..00bbb44 100644 --- a/src/TUnit/manifest.php +++ b/src/TUnit/manifest.php @@ -3,7 +3,7 @@ /** * Autoload manifest * - * Autogenerated by manifester.php on 2009-06-15 20:40:53 + * Autogenerated by manifester.php on 2009-06-16 22:58:05 * * @package TUnit * @version 0.1.0 @@ -30,8 +30,10 @@ 'IdenticalConstraint' => 'TUnit/framework/constraints/IdenticalConstraint.php', 'IgnoredTest' => 'TUnit/framework/result/FailedTest.php', 'IgnoredTestResult' => 'TUnit/framework/result/IgnoredTestResult.php', + 'InvocationExpectation' => 'TUnit/framework/mock/InvocationExpectation.php', 'InvocationTracker' => 'TUnit/framework/mock/InvocationTracker.php', 'IssetConstraint' => 'TUnit/framework/constraints/SimpleConstraints.php', + 'MockHandler' => 'TUnit/framework/mock/MockHandler.php', 'MockInvocation' => 'TUnit/framework/mock/MockInvocation.php', 'MockObject' => 'TUnit/framework/mock/MockObject.php', 'MockObjectCreator' => 'TUnit/framework/mock/MockObject.php',