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

View File

@ -41,8 +41,13 @@
public function afterTestRunner(TestRunner $runner) { public function afterTestRunner(TestRunner $runner) {
$elapsedTime = $runner->getEndTime() - $runner->getStartTime(); $elapsedTime = $runner->getEndTime() - $runner->getStartTime();
$numTests = (count($runner) === 1) ? '1 test' : count($runner) . ' tests'; $testCount = $runner->getTestCount();
$this->out('Ran ' . $numTests . ' in ' . round($elapsedTime, 3) . ' seconds' . "\n"); //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) { public function beforeTestSuite(TestSuite $suite) {

View File

@ -5,13 +5,16 @@
protected $name; protected $name;
private $autoVerify; private $autoVerify;
private $testableMethods;
const ANY = -1; const ANY = -1;
const AT_LEAST_ONCE = -2; const AT_LEAST_ONCE = -2;
public function __construct($name) { public function __construct($name) {
$this->name = $name; $this->name = $name;
$this->autoVerify = true; $this->autoVerify = true;
$this->testableMethods = array();
} }
public final function getName() { public final function getName() {
@ -26,11 +29,11 @@
$this->autoVerify = (bool)$autoVerify; $this->autoVerify = (bool)$autoVerify;
} }
public function setUp() { protected function setUp() {
} }
public function tearDown() { protected function tearDown() {
} }
@ -40,9 +43,10 @@
} }
$result = new CombinedTestResult(); $result = new CombinedTestResult();
foreach ($this->getTestableMethods() as $method) { foreach ($this->getTestableMethods() as $testMethod) {
$testMethod = new TestMethod($this, $method); $this->setUp();
$result->addTestResult($testMethod->run($listeners)); $result->addTestResult($testMethod->run($listeners));
$this->tearDown();
} }
foreach ($listeners as $listener) { foreach ($listeners as $listener) {
@ -52,16 +56,23 @@
return $result; return $result;
} }
protected final function getTestableMethods() { public final function getTestableMethods() {
$refClass = new ReflectionClass($this); if (empty($this->testableMethods)) {
$methods = array(); $refClass = new ReflectionClass($this);
foreach ($refClass->getMethods() as $method) { $methods = array();
if (preg_match('/^[\/\*\s]*@test\s*(?:\*\/)?$/m', $method->getDocComment())) { foreach ($refClass->getMethods() as $method) {
$methods[] = $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) { protected function createMockObject($className, array $methods = array(), array $args = array(), $name = '', $callParent = true) {
@ -86,6 +97,14 @@
throw new FailedTest($message); 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 { /* internal */ class TestMethod implements Testable {
protected $testCase; protected $autoVerify;
protected $method; protected $closure;
protected $name;
public function __construct(TestCase $testCase, ReflectionMethod $method) { public function __construct($closure, $name, $autoVerify) {
$this->testCase = $testCase; $this->closure = $closure;
$this->method = $method; $this->autoVerify = (bool)$autoVerify;
$this->name = $name;
} }
public function getName() { public function getName() {
return $this->method->getDeclaringClass()->getName() . '::' . $this->method->getName(); return $this->name;
} }
public function run(array $listeners) { public function run(array $listeners) {
@ -22,12 +24,11 @@
$result = null; $result = null;
$failure = null; $failure = null;
$this->testCase->setUp();
try { try {
$this->method->invoke($this->testCase); call_user_func($this->closure);
//verify if necessary //verify if necessary
if ($this->testCase->getAutoVerify()) { if ($this->autoVerify) {
foreach (MockRegistry::getTrackers() as $tracker) { foreach (MockRegistry::getTrackers() as $tracker) {
if (!$tracker->verify()) { if (!$tracker->verify()) {
throw new FailedTest('Verification of InvocationTracker failed'); throw new FailedTest('Verification of InvocationTracker failed');
@ -50,7 +51,6 @@
} }
$result = $this->createTestResult($failure); $result = $this->createTestResult($failure);
$this->testCase->tearDown();
foreach ($listeners as $listener) { foreach ($listeners as $listener) {
$listener->afterTestMethod($this); $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; $this->tests[] = $test;
return $this; return $this;
} }
public final function getTests() {
return $this->tests;
}
public function run(array $listeners) { public function run(array $listeners) {
foreach ($listeners as $listener) { foreach ($listeners as $listener) {
$listener->beforeTestSuite($this); $listener->beforeTestSuite($this);
@ -53,6 +57,14 @@
return $result; return $result;
} }
public function count() {
return count($this->tests);
}
public function getTestCount() {
return Util::countTests($this->tests);
}
} }
?> ?>

View File

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

View File

@ -86,6 +86,57 @@
return $flattened; 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;
}
} }
?> ?>