I have test suites with lots of tests in them. here is a medium sized one:
ok 4 - CommodityBasketTest::testStartsOutEmpty
ok 5 - CommodityBasketTest::testCanAddACommodity
ok 6 - CommodityBasketTest::testWillAddOneCommodityByDefault
ok 7 - CommodityBasketTest::testCannotAddACommodityWithAnNonNumericQuantity
ok 8 - CommodityBasketTest::testAddingTheSameCommodityWillIncreaseItsQuantity
ok 9 - CommodityBasketTest::testMultipleCommodityCanBeAdded
ok 10 - CommodityBasketTest::testTakingFromAnEmptyBasketWontWork
ok 11 - CommodityBasketTest::testTakesFirstCommodityFromTheBasket
ok 12 - CommodityBasketTest::testCanRetrieveASpecificCommodity
ok 13 - CommodityBasketTest::testWillThrowExceptionOnMissingCommodity
ok 14 - CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets
ok 15 - CommodityBasketTest::testReturnsProperWorthOfACommodity
ok 16 - CommodityBasketTest::testWillAccuratelyReturnStatistics
How can I rig PHPUnit so that I can somehow display the underlying method being tested like I have it in the paste?? I'm flexible on output; I'd just like to know that CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets
tests CommodityBasket::getValuation()
, for instance.
This is what I'd like:
-- CommodityBasket::__construct() --
ok 4 - CommodityBasketTest::testStartsOutEmpty
-- CommodityBasket::add() --
ok 5 - CommodityBasketTest::testCanAddACommodity
ok 6 - CommodityBasketTest::testWillAddOneCommodityByDefault
ok 7 - CommodityBasketTest::testCannotAddACommodityWithAnNonNumericQuantity
ok 8 - CommodityBasketTest::testAddingTheSameCommodityWillIncreaseItsQuantity
ok 9 - CommodityBasketTest::testMultipleCommodityCanBeAdded
-- CommodityBasket::take() --
ok 10 - CommodityBasketTest::testTakingFromAnEmptyBasketWontWork
ok 11 - CommodityBasketTest::testTakesFirstCommodityFromTheBasket
ok 12 - CommodityBasketTest::testCanRetrieveASpecificCommodity
ok 13 - CommodityBasketTest::testWillThrowExceptionOnMissingCommodity
-- CommodityBasket::getValuation() --
ok 14 - CommodityBasketTest::testReturnsZeroWorthForEmptyBaskets
ok 15 - CommodityBasketTest::testReturnsProperWorthOfACommodity
-- CommodityBasket::dumpStats() --
ok 16 - CommodityBasketTest::testWillAccuratelyReturnStatistics
Thank you for your suggestions.
My approach would be a combination of the @covers
Tag and a custom result printer.
You should use the @covers tag anyways to generate more meaningful code coverage, especially in bigger test suites it is important to make sure that only the tests that are supposed to test a method really generate coverage for it.
I know your question isn't related to coverage but we'll get to that in a minute. And maybe just using that annotation is enough for you as every method that doesn't have a test dedicated to it will show 0% coverage no matter if you run all your integration tests and so forth.
The implementation can surly be tuned, i just wanted to produce something that works reasonably well to show of the concept and hopefully give you something you can adapt.
The code is alpha as I've written it only for that question but it works with the current phpunit and I think i pasted everything you need.
--- myClass ---
-- myClass::a --
ok - myClassTest::testAone
fail - myClassTest::testAtwoFails
-- myClass::b --
ok - myClassTest::testB
-- myClass::untested --
!! Method untested !!
I hope this matches your desired output. The formatting can be changed rather easily in the code below.
It prints this piece of information for every class you have a test for (an empty one is enough!)
If one tests @covers multiple methods it will show up for EVERY ONE of those methods
<?php
class myClass {
public function a() {
return 1;
}
public function b() {
return 2;
}
public function untested() {
return 3;
}
}
<?php
require_once("myClass.php");
class myClassTest extends PHPUnit_Framework_TestCase {
/**
* @covers a
*/
public function testAone() {
$sut = new myClass();
$this->assertSame(1, $sut->a());
}
/**
* @covers a
*/
public function testAtwoFails() {
$sut = new myClass();
$this->assertSame("error", $sut->a());
}
/**
* @covers b
*/
public function testB() {
$sut = new myClass();
$this->assertSame(2, $sut->b());
}
}
<phpunit>
<listeners>
<listener class="ResultPrinterListener" file="./ResultPrinterListener.php"></listener>
</listeners>
</phpunit>
<?php
class ResultPrinterListener implements PHPUnit_Framework_TestListener {
protected $suites = array();
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {}
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {}
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
public function startTest(PHPUnit_Framework_Test $test) {}
public function endTest(PHPUnit_Framework_Test $test, $time) {}
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}
public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {
$this->suites[] = $suite;
}
public function __destruct() {
$tests = array();
foreach($this->suites as $suite) {
foreach($suite->tests() as $test) {
if(!$test instanceOf PHPUnit_Framework_TestCase) {
continue;
}
$testClass = get_class($test);
$classUnderTest = substr($testClass, 0, -4); // just cutting the "Test" for now
/**
* Create an array structue
* array[ClassUnderTests][methodUnderTest][arrayOfTestMethodsThatTestThatMethod]
* Every method for a class you have at least one test for will show up here for now
*/
if(!isset($tests[$classUnderTest])) {
if(!class_exists($classUnderTest)) {
echo "\nCan't find matching class '$classUnderTest' for test class $testClass!\n";
}
$class = new ReflectionClass($classUnderTest);
foreach($class->getMethods() as $method) {
$tests[$classUnderTest][$method->getName()] = array();
}
}
$annotations = $test->getAnnotations();
if(!isset($annotations["method"]["covers"])) {
continue;
}
foreach($annotations["method"]["covers"] as $functionUnderTest) {
$statusLine = "";
$status = $test->getStatus();
if($status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
$statusLine .= "fail - ";
} else if($status == PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED) {
$statusLine .= "skip - ";
} else if($status == PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE) {
$statusLine .= "inc - ";
} else {
$statusLine .= "ok - ";
}
$statusLine .= $testClass."::".$test->getName();
$tests[$classUnderTest][$functionUnderTest][] = $statusLine;
}
}
}
foreach($tests as $classUnderTest => $methods) {
echo "\n\n --- $classUnderTest --- \n\n";
foreach($methods as $method => $testCaseStrings) {
echo "-- $classUnderTest::$method -- \n";
if($testCaseStrings == array()) {
echo " !! Method untested !!\n";
continue;
}
foreach($testCaseStrings as $testCaseString) {
echo " $testCaseString\n";
}
echo "\n";
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With