Мой подход - комбинация тега @covers
и пользовательского принтера .
Вы должны использовать тег @covers в любом случае, чтобы генерировать более значимое покрытие кода, особенно в больших наборах тестов. Важно убедиться, что только тесты, которые предполагаются для тестирования метод действительно генерирует покрытие для него.
Я знаю, что ваш вопрос не связан с освещением, но мы вернемся к этому через минуту. И, может быть, вам достаточно просто использовать эту аннотацию, поскольку каждый метод, для которого нет выделенного теста, будет показывать покрытие в 0% независимо от того, выполняете ли вы все интеграционные тесты и т. Д.
Пользовательский обработчик результатов для сбора необходимой информации
Реализация может быть настроена непринужденно, я просто хотел создать что-то, что работает достаточно хорошо, чтобы показать концепцию и, надеюсь, дать вам то, что вы можете адаптировать.
Код альфа, поскольку я написал его только для этого вопроса, но он работает с текущим phpunit, и я думаю, что вставил все, что вам нужно.
Результат:
--- myClass ---
-- myClass::a --
ok - myClassTest::testAone
fail - myClassTest::testAtwoFails
-- myClass::b --
ok - myClassTest::testB
-- myClass::untested --
!! Method untested !!
Надеюсь, это соответствует желаемому результату. Форматирование можно легко изменить в приведенном ниже коде.
Печатает эту информацию для каждого класса, для которого у вас есть тест (достаточно пустого!)
Если один из тестов @ покрывает несколько методов, он появится для КАЖДОГО из этих методов
Тестируемый класс
<?php
class myClass {
public function a() {
return 1;
}
public function b() {
return 2;
}
public function untested() {
return 3;
}
}
The Testcase
<?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.xml, необходимый для регистрации тестового слушателя!
<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";
}
}
}
}