This commit is contained in:
tmont 2009-06-28 00:48:35 +00:00
parent 1e6afd52e5
commit 10f258f2f9
3 changed files with 333 additions and 6 deletions

View File

@ -1,12 +1,64 @@
<?php
/**
* MockInvocation
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
/**
* Struct representing an invocation of mocked method
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
class MockInvocation {
/**
* The name of the mocked class on which the method was invoked
*
* @var string
*/
protected $className;
/**
* Name of the invoked method
*
* @var string
*/
protected $method;
/**
* The arguments passed to the method upon invocation
*
* @var array
*/
protected $args;
/**
* The number of times this method was invoked
*
* @var int
*/
protected $count;
/**
* Constructor
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $className
* @param string $methodName
* @param array $args
* @param int $count
*/
public function __construct($className, $methodName, array $args, $count) {
$this->className = $className;
$this->method = $methodName;
@ -14,18 +66,54 @@
$this->count = $count;
}
/**
* Gets the name of the method
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* Gets the arguments
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @return array
*/
public function getArgs() {
return $this->args;
}
/**
* Gets the name of the class
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @return mixed
*/
public function getClass() {
return $this->className;
}
/**
* Gets the count
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @return mixed
*/
public function getCount() {
return $this->count;
}

View File

@ -1,15 +1,60 @@
<?php
/**
* File containing MockObject, MockObjectCreator
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
/**
* MockObject dummy interface
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
interface MockObject {}
/**
* Class MockObjectCreator
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
class MockObjectCreator {
/**
* The object to mock
*
* @var ReflectionClass
*/
protected $referenceObject;
/**
* The methods to mock
*
* @var mixed
*/
protected $methods;
protected static $registry = array();
protected static $expectations = array();
/**
* Constructor
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $class The name of the class or interface to mock
* @param bool $callParent Whether to call the parent constructor
* @throws InvalidArgumentException
* @throws LogicException
*/
public function __construct($class, $callParent = true) {
if (!class_exists($class) && !interface_exists($class)) {
throw new InvalidArgumentException('The class "' . $class . '" does not exist');
@ -33,6 +78,20 @@
);
}
/**
* Adds a method to mock
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @uses methodIsMockable()
*
* @param string $methodName Name of the method to mock
* @param bool $callParent Whether to call the parent
* @param string $body Custom PHP code to execute in the method body
* @throws LogicException if the method is not mockable
* @return MockObjectCreator
*/
public function addMethod($methodName, $callParent = false, $body = '') {
$methodType = 'generic';
if ($this->referenceObject->hasMethod($methodName)) {
@ -58,10 +117,35 @@
return $this;
}
protected function methodIsMockable(ReflectionMethod $method) {
/**
* Determines if a method is mockable
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param ReflectionMethod $method
* @return bool
*/
protected final function methodIsMockable(ReflectionMethod $method) {
return !$method->isFinal() && !$method->isPrivate() && !$method->isStatic();
}
/**
* Generates the mock object
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @uses generateClassDefinition()
* @uses MockRegistry::addClass()
*
* @param array $constructorArgs Arguments to pass to the constructor
* @param string $name Custom name to give the newly created mock object, by default
* a random, unused name is chosen
* @throws LogicException if a name is given that is already in use
* @return object
*/
public function generate(array $constructorArgs = array(), $name = '') {
if (empty($name)) {
$className = $this->referenceObject->getName();
@ -71,7 +155,7 @@
}
if (class_exists($name) || interface_exists($name)) {
throw new RuntimeException('Cannot use the name "' . $name . '" for mock object because the class or interface already exists');
throw new LogicException('Cannot use the name "' . $name . '" for mock object because the class or interface already exists');
}
$code = $this->generateClassDefinition($name);
@ -84,6 +168,17 @@
return $obj;
}
/**
* Generates the class definition of the mock object
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @uses generateMethodDefinition()
*
* @param string $name The name of the class
* @return string Executable PHP code that will generate a class when eval()'d
*/
protected function generateClassDefinition($name) {
$code = 'class ' . $name . ' ';
if ($this->referenceObject->isInterface()) {
@ -104,6 +199,18 @@
return $code;
}
/**
* Generates a method definition
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $type "default" or "generic"
* @param string $name Name of the method
* @param array $methodData Should have keys "call_parent" and "body"
* @return string
*/
protected function generateMethodDefinition($type, $name, array $methodData) {
$modifier = 'public';
$params = '';

View File

@ -1,22 +1,85 @@
<?php
/**
* MockRegistry
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
/**
* Registry of mock objects
*
* @package TUnit
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
class MockRegistry {
/**
* Array of {@link InvocationTracker}s
*
* @var array
*/
private static $trackers = array();
/**
* Array of {@link InvocationExpectation}s
*
* @var array
*/
private static $expectations = array();
/**
* Constructor
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @ignore
*/
private function __construct() {}
/**
* Resets the registry
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*/
public static function reset() {
self::$trackers = array();
self::$expectations = array();
}
/**
* Adds a class to the registry and registers a tracker for it
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $className
*/
public static function addClass($className) {
self::$trackers[$className] = new InvocationTracker();
self::$expectations[$className] = array();
}
/**
* Adds an expectation for the specified class
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $className
* @param InvocationExpectation $expectation
* @throws LogicException if the class does not exist in the registry
*/
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');
@ -25,10 +88,31 @@
self::$expectations[$className][] = $expectation;
}
/**
* Gets all registered expectations
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @uses Util::arrayFlatten()
*
* @return array
*/
public static function getAllExpectations() {
return Util::arrayFlatten(self::$expectations);
}
/**
* Gets all registered expectations for the specified class
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $className
* @throws LogicException if the class does not exist in the registry
* @return array
*/
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');
@ -37,6 +121,21 @@
return self::$expectations[$className];
}
/**
* Registers an invocation for the specified class
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @uses getInvocationCount()
* @uses InvocationTracker::registerInvocation()
*
* @param string $className
* @param string $methodName
* @param array $args
* @throws LogicException if the class does not exist in the registry
* @return InvocationExpectation|false Returns the matched invocation, or false if no match
*/
public static function registerInvocation($className, $methodName, array $args) {
if (!isset(self::$trackers[$className])) {
throw new LogicException('Unable to register invocation because the object does not exist in the registry');
@ -47,6 +146,17 @@
return $expectation;
}
/**
* Gets the tracker for the specified class
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @param string $name
* @throws LogicException if the class does not exist in the registry
* @return InvocationTracker
*/
public static function getTracker($name) {
if (!isset(self::$trackers[$name])) {
throw new LogicException('Unable to retrieve invocation tracker because the object does not exist in the registry');
@ -55,10 +165,32 @@
return self::$trackers[$name];
}
/**
* Gets all registered {@link InvocationTracker}s
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
*
* @return array
*/
public static function getTrackers() {
return self::$trackers;
}
/**
* Gets the current invocation count for the class and method
*
* @author Tommy Montgomery
* @version 1.0
* @since 1.0
* @uses InvocationTracker::getInvocations()
* @uses MockInvocation::getMethod()
*
* @param string $className
* @param string $methodName
* @return int
*/
private static function getInvocationCount($className, $methodName) {
$count = 0;
foreach (self::$trackers[$className]->getInvocations() as $invocation) {