* overhauled test method (now it uses closures)

* implemented RecursivelyCountable in TestSuite/TestCase/TestMethod
This commit is contained in:
tmont 2009-06-20 05:44:02 +00:00
parent b4837595ee
commit cd6df1ffbd
7 changed files with 135 additions and 28 deletions

View File

@ -1,6 +1,6 @@
<?php
abstract class TestRunner implements Countable {
abstract class TestRunner implements RecursivelyCountable {
protected $tests;
protected $listeners;
@ -26,6 +26,10 @@
return $this->endTime;
}
public final function getTests() {
return $this->tests;
}
public final function warn($message) {
foreach ($this->listeners as $listener) {
$listener->onFrameworkWarning($message);
@ -111,6 +115,10 @@
return count($this->tests);
}
public function getTestCount() {
return Util::countTests($this->tests);
}
protected abstract function getAllowableOptions();
}

View File

@ -41,8 +41,13 @@
public function afterTestRunner(TestRunner $runner) {
$elapsedTime = $runner->getEndTime() - $runner->getStartTime();
$numTests = (count($runner) === 1) ? '1 test' : count($runner) . ' tests';
$this->out('Ran ' . $numTests . ' in ' . round($elapsedTime, 3) . ' seconds' . "\n");
$testCount = $runner->getTestCount();
//print_r($runner->getTests());
$suites = $testCount['suite'] === 1 ? '1 test suite' : $testCount['suite'] . ' test suites';
$cases = $testCount['case'] === 1 ? '1 test case' : $testCount['case'] . ' test cases';
$methods = $testCount['method'] === 1 ? '1 test method' : $testCount['method'] . ' test methods';
$this->out("Ran $suites, $cases and $methods in " . round($elapsedTime, 3) . ' seconds' . "\n");
}
public function beforeTestSuite(TestSuite $suite) {

View File

@ -5,13 +5,16 @@
protected $name;
private $autoVerify;
private $testableMethods;
const ANY = -1;
const AT_LEAST_ONCE = -2;
public function __construct($name) {
$this->name = $name;
$this->autoVerify = true;
$this->name = $name;
$this->autoVerify = true;
$this->testableMethods = array();
}
public final function getName() {
@ -26,11 +29,11 @@
$this->autoVerify = (bool)$autoVerify;
}
public function setUp() {
protected function setUp() {
}
public function tearDown() {
protected function tearDown() {
}
@ -40,9 +43,10 @@
}
$result = new CombinedTestResult();
foreach ($this->getTestableMethods() as $method) {
$testMethod = new TestMethod($this, $method);
foreach ($this->getTestableMethods() as $testMethod) {
$this->setUp();
$result->addTestResult($testMethod->run($listeners));
$this->tearDown();
}
foreach ($listeners as $listener) {
@ -52,16 +56,23 @@
return $result;
}
protected final function getTestableMethods() {
$refClass = new ReflectionClass($this);
$methods = array();
foreach ($refClass->getMethods() as $method) {
if (preg_match('/^[\/\*\s]*@test\s*(?:\*\/)?$/m', $method->getDocComment())) {
$methods[] = $method;
public final function getTestableMethods() {
if (empty($this->testableMethods)) {
$refClass = new ReflectionClass($this);
$methods = array();
foreach ($refClass->getMethods() as $method) {
if (
$method->getDeclaringClass()->getName() !== __CLASS__ &&
preg_match('/^[\/\*\s]*@test\s*(?:\*\/)?$/m', $method->getDocComment())
) {
$methods[] = new TestMethod(Util::getClosure($this, $method->getName()), get_class($this) . '::' . $method->getName(), $this->getAutoVerify());
}
}
$this->testableMethods = $methods;
}
return $methods;
return $this->testableMethods;
}
protected function createMockObject($className, array $methods = array(), array $args = array(), $name = '', $callParent = true) {
@ -86,6 +97,14 @@
throw new FailedTest($message);
}
public function count() {
return count($this->getTestableMethods());
}
public function getTestCount() {
return Util::countTests($this->testableMethods);
}
}
?>

View File

@ -2,16 +2,18 @@
/* internal */ class TestMethod implements Testable {
protected $testCase;
protected $method;
protected $autoVerify;
protected $closure;
protected $name;
public function __construct(TestCase $testCase, ReflectionMethod $method) {
$this->testCase = $testCase;
$this->method = $method;
public function __construct($closure, $name, $autoVerify) {
$this->closure = $closure;
$this->autoVerify = (bool)$autoVerify;
$this->name = $name;
}
public function getName() {
return $this->method->getDeclaringClass()->getName() . '::' . $this->method->getName();
return $this->name;
}
public function run(array $listeners) {
@ -22,12 +24,11 @@
$result = null;
$failure = null;
$this->testCase->setUp();
try {
$this->method->invoke($this->testCase);
call_user_func($this->closure);
//verify if necessary
if ($this->testCase->getAutoVerify()) {
if ($this->autoVerify) {
foreach (MockRegistry::getTrackers() as $tracker) {
if (!$tracker->verify()) {
throw new FailedTest('Verification of InvocationTracker failed');
@ -50,7 +51,6 @@
}
$result = $this->createTestResult($failure);
$this->testCase->tearDown();
foreach ($listeners as $listener) {
$listener->afterTestMethod($this);
@ -92,6 +92,14 @@
}
}
public function count() {
return 1;
}
public function getTestCount() {
return Util::countTests(array($this));
}
}
?>

View File

@ -22,11 +22,15 @@
}
public function addTest(Testable $test) {
public final function addTest(Testable $test) {
$this->tests[] = $test;
return $this;
}
public final function getTests() {
return $this->tests;
}
public function run(array $listeners) {
foreach ($listeners as $listener) {
$listener->beforeTestSuite($this);
@ -53,6 +57,14 @@
return $result;
}
public function count() {
return count($this->tests);
}
public function getTestCount() {
return Util::countTests($this->tests);
}
}
?>

View File

@ -1,6 +1,10 @@
<?php
interface Testable {
interface RecursivelyCountable extends Countable {
public function getTestCount();
}
interface Testable extends RecursivelyCountable {
public function run(array $listeners);

View File

@ -86,6 +86,57 @@
return $flattened;
}
public static function countTests(array $tests) {
$counts = array(
'suite' => 0,
'case' => 0,
'method' => 0
);
foreach ($tests as $test) {
if ($test instanceof TestSuite) {
$counts['suite']++;
$counts = self::mergeTestCount($counts, self::countTests($test->getTests()));
} else if ($test instanceof TestCase) {
$counts['case']++;
$counts = self::mergeTestCount($counts, self::countTests($test->getTestableMethods()));
} else if ($test instanceof TestMethod) {
$counts['method']++;
}
}
return $counts;
}
private static function mergeTestCount(array $arr1, array $arr2) {
$arr1['suite'] += $arr2['suite'];
$arr1['case'] += $arr2['case'];
$arr1['method'] += $arr2['method'];
return $arr1;
}
public static function getClosure($object, $method) {
if (!is_object($object)) {
throw new InvalidArgumentException('1st argument must be an object');
}
$closure = create_function('',
'
$args = func_get_args();
static $obj = null;
if ($obj === null && isset($args[0]) && is_object($args[0])) {
$obj = $args[0];
return;
}
return call_user_func_array(array($obj, \'' . $method . '\'), $args);'
);
$closure($object);
return $closure;
}
}
?>