Покрытие кода PHPUnit показывает покрытие 0%

Для одного из моей библиотеки ( источник ), Я настроил PHPunit следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" colors="true">
<testsuites>
<testsuite name="PHPReboot Stopwatch Test Suite">
<directory>tests/Phpreboot/Stopwatch</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/Phpreboot/Stopwatch</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="./log/codeCoverage" charset="UTF-8"yui="true" highlight="true"lowUpperBound="50" highLowerBound="80"/>
<log type="testdox-html" target="./log/testdox.html" />
</logging>
</phpunit>

В выше источник, phpunit.xml.dist просто копируется как phpunit.xml.

Мой отчет о покрытии кода генерируется, но отчеты показывают покрытие кода 0%. Однако, основываясь на тестах (проверьте код в исходном коде), я уверен, что оно должно быть больше 0%.

Может кто-нибудь предложить, пожалуйста, где моя конфигурация идет не так?

Изменить после первого комментария

Testcases: https://github.com/phpreboot/stopwatch/blob/master/tests/Phpreboot/Stopwatch/StopWatchTest.php

<?php

namespace Phpunit\Stopwatch;

use Phpreboot\Stopwatch\StopWatch;
use Phpreboot\Stopwatch\Timer;

/**
* Class StopWatchTest
* @package Phpunit\Stopwatch
* @group Phpreboot
* @group Phpreboot_Stopwatch
* @group Phpreboot_Stopwatch_StopWatch
*/
class StopWatchTest extends \PHPUnit_Framework_TestCase
{
/** @var  StopWatch $stopWatch */
private $stopWatch;

public function setUp()
{
$this->stopWatch = new StopWatch();
}

public function tearDown()
{
$this->stopWatch = null;
}

/* ******************/
/* Constructor test */
/* ******************/
/**
* @group Phpreboot_Stopwatch_StopWatch_constructor
*/
public function testStopWatchHaveDefaultWatch()
{
/** @var Timer $defaultWatch */
$defaultWatch = $this->stopWatch->getWatch();

$this->assertNotNull($defaultWatch, "No watch available");
$this->assertInstanceOf('Phpreboot\Stopwatch\Timer', $defaultWatch, "Not an instance of Watch");

$name = $defaultWatch->getName();

$this->assertEquals(StopWatch::STOPWATCH_DEFAULT_NAME, $name, "Default name of StopWatch is not set correctly");
}

/* ***************/
/* addWatch Test */
/* ***************/
/**
* @group Phpreboot_Stopwatch_StopWatch_addWatch
*/
public function testWatchCanBeAdded()
{
$this->assertEquals(1, $this->stopWatch->getWatchCount(), "Stopwatch doesn't initialized with default watch.");

$this->stopWatch->addWatch('testWatch');
$this->assertEquals(2, $this->stopWatch->getWatchCount(), "Stopwatch could not be added");
}

/**
* @group Phpreboot_Stopwatch_StopWatch_addWatch
*/
public function testWatchCanNotBeAddedWithDuplicateName()
{
$this->assertEquals(1, $this->stopWatch->getWatchCount(), "Stopwatch doesn't initialized with default watch.");
$this->assertFalse($this->stopWatch->addWatch(StopWatch::STOPWATCH_DEFAULT_NAME), "Watch with default name was duplicated.");
$this->assertEquals(1, $this->stopWatch->getWatchCount(), "Watch with default name was duplicated.");

$this->assertTrue($this->stopWatch->addWatch('testWatch'), "New watch couldn't be added.");
$this->assertEquals(2, $this->stopWatch->getWatchCount(), "New watch couldn't be added.");
$this->assertFalse($this->stopWatch->addWatch('testWatch'), "New watch with duplicate name was added.");
$this->assertEquals(2, $this->stopWatch->getWatchCount(), "New watch with duplicate name was added.");
}

/* ********************/
/* getWatchCount Test */
/* ********************/
/**
* @group Phpreboot_Stopwatch_StopWatch_getWatchCount
*/
public function testWatchCountIsCorrect()
{
$totalWatch = $this->stopWatch->getWatchCount();

$this->assertEquals(1, $totalWatch, "Watch count is not correct");
}

/* ***************/
/* getWatch Test */
/* ***************/
/**
* @group Phpreboot_Stopwatch_StopWatch_getWatch
*/
public function testDefaultWatchCouldBeReturned()
{
$watch = $this->stopWatch->getWatch();
$this->assertInstanceOf('Phpreboot\Stopwatch\Timer', $watch, "Default watch is not an instance of Watch.");
$this->assertEquals(StopWatch::STOPWATCH_DEFAULT_NAME, $watch->getName(), "Name of default was was not correctly set.");
}

/**
* @group Phpreboot_Stopwatch_StopWatch_getWatch
*/
public function testWatchCouldBeReturned()
{
$this->stopWatch->addWatch('newWatch');

$newWatch = $this->stopWatch->getWatch("newWatch");
$this->assertInstanceOf('Phpreboot\Stopwatch\Timer', $newWatch, "New watch is not an instance of Watch.");
}
}

Источник: https://github.com/phpreboot/stopwatch/blob/master/src/Phpreboot/Stopwatch/StopWatch.php

/*
* This file is part of the PHPReboot/Stopwatch package.
*
* (c) Kapil Sharma <kapil@phpreboot.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Phpreboot\Stopwatch;

use Phpreboot\Stopwatch\Timer;

class StopWatch
{
const STOPWATCH_DEFAULT_NAME = "default_watch_R@nd0m_n@m3";

private $timers;

/**
* Constructor to create new StopWatch instance with default watch.
*/
public function __construct()
{
$this->timers = array();
$this->addWatch(self::STOPWATCH_DEFAULT_NAME);
}

public function start($name = self::STOPWATCH_DEFAULT_NAME)
{
if (!$this->isWatchExist($name)) {
return false;
}

return $this->getWatch($name)->start();
}

public function pause($name = self::STOPWATCH_DEFAULT_NAME)
{
if (!$this->isWatchExist($name)) {
return false;
}

return $this->getWatch($name)->pause();
}

public function stop($name = self::STOPWATCH_DEFAULT_NAME)
{
if (!$this->isWatchExist($name)) {
return false;
}

return $this->getWatch($name)->stop();
}

public function getTime($name = self::STOPWATCH_DEFAULT_NAME)
{
if (!$this->isWatchExist($name)) {
return -1;
}

return $this->getWatch($name)->getTime();
}

public function isWatchExist($name)
{
return array_key_exists($name, $this->timers);
}

/**
* Add a new watch to the StopWatch.
*
* @param string $name Name of watch to be added.
* @return bool True if watch added successfully, false otherwise.
*/
public function addWatch($name)
{
if (array_key_exists($name, $this->timers)) {
return false;
}

$watch = new Timer($name);
$this->timers[$name] = $watch;

return true;
}

public function addWatches(array $watches)
{
$isWatchAdded = false;

if (empty($watches)) {
return $isWatchAdded;
}

foreach ($watches as $watch) {
$this->addWatch($watch);
$isWatchAdded = true;
}

return $isWatchAdded;
}

/**
* Get a watch by name of watch.
*
* @param string $name Name of watch
* @throws \InvalidArgumentException In case watch with name '$name' does not exist.
* @return Timer A watch instance with name '$name'.
*/
public function getWatch($name = self::STOPWATCH_DEFAULT_NAME)
{
if (!array_key_exists($name, $this->timers)) {
throw new \InvalidArgumentException('Watch ' . $name . ' does not exist.');
}

return $this->timers[$name];
}

public function getWatchCount()
{
return count($this->timers);
}
}

Редактировать 2: настройки XDebug

php --info | grep xdebug
/etc/php5/cli/conf.d/20-xdebug.ini,
xdebug
xdebug support => enabled
xdebug.auto_trace => Off => Off
xdebug.cli_color => 0 => 0
xdebug.collect_assignments => Off => Off
xdebug.collect_includes => On => On
xdebug.collect_params => 0 => 0
xdebug.collect_return => Off => Off
xdebug.collect_vars => Off => Off
xdebug.coverage_enable => On => On
xdebug.default_enable => On => On
xdebug.dump.COOKIE => no value => no value
xdebug.dump.ENV => no value => no value
xdebug.dump.FILES => no value => no value
xdebug.dump.GET => no value => no value
xdebug.dump.POST => no value => no value
xdebug.dump.REQUEST => no value => no value
xdebug.dump.SERVER => no value => no value
xdebug.dump.SESSION => no value => no value
xdebug.dump_globals => On => On
xdebug.dump_once => On => On
xdebug.dump_undefined => Off => Off
xdebug.extended_info => On => On
xdebug.file_link_format => no value => no value
xdebug.idekey => no value => no value
xdebug.max_nesting_level => 250 => 250
xdebug.overload_var_dump => On => On
xdebug.profiler_aggregate => Off => Off
xdebug.profiler_append => Off => Off
xdebug.profiler_enable => Off => Off
xdebug.profiler_enable_trigger => Off => Off
xdebug.profiler_output_dir => /tmp => /tmp
xdebug.profiler_output_name => cachegrind.out.%p => cachegrind.out.%p
xdebug.remote_autostart => Off => Off
xdebug.remote_connect_back => On => On
xdebug.remote_cookie_expire_time => 3600 => 3600
xdebug.remote_enable => On => On
xdebug.remote_handler => dbgp => dbgp
xdebug.remote_host => localhost => localhost
xdebug.remote_log => no value => no value
xdebug.remote_mode => req => req
xdebug.remote_port => 9000 => 9000
xdebug.scream => Off => Off
xdebug.show_exception_trace => Off => Off
xdebug.show_local_vars => Off => Off
xdebug.show_mem_delta => Off => Off
xdebug.trace_enable_trigger => Off => Off
xdebug.trace_format => 0 => 0
xdebug.trace_options => 0 => 0
xdebug.trace_output_dir => /tmp => /tmp
xdebug.trace_output_name => trace.%c => trace.%c
xdebug.var_display_max_children => 128 => 128
xdebug.var_display_max_data => 512 => 512
xdebug.var_display_max_depth => 3 => 3

1

Решение

В комментариях к каждому тесту на @covers аннотирование сообщить PHPUnit, какой код рассматривается в этом тесте.

Аннотация @covers может использоваться в тестовом коде, чтобы указать, какие
Метод (ы), который тестовый метод хочет проверить:

/**
* @covers BankAccount::getBalance
*/
public function testBalanceIsInitiallyZero()
{
$this->assertEquals(0, $this->ba->getBalance());
}

Если предоставлено, только информация покрытия кода для указанного
метод (ы) будут рассмотрены.

Также убедитесь, что вы установили Xdebug как это требуется для работы отчета о покрытии кода.

2

Другие решения

У меня похожая проблема. В моем случае я был уверен, что метод тестировался, потому что в отчете о покрытии все строки были зелеными (что означает, что тест проходит через них), но для некоторых методов в сводке отображалось 0%, единственная странная вещь в моем коде была глубокий вложенный массив с таким отступом:

$array['key']
['foo']
['bar']
['fooz']
['bazz'] = "some_value";

после того, как сделать это сингл, как он работал

$array['key']['foo']['bar']['fooz']['bazz'] = "some_value";

это была странная проблема со странным обходом

Надеюсь, поможет

2

Вы должны проверить, что параметр filter-> whitelist действительно включает исходные файлы, которые вы хотите проанализировать по охвату кода.

0

Я хочу добавить еще одну возможную причину, по которой все настроено правильно, но покрытие кода просто 0%. Это происходит, когда вы помещаете все вызовы тестового кода в PHPUnit setUp() а также setUpBeforeClass() методы. В реальных методах тестирования вы все еще можете проверить сгенерированный результат, но это не влияет на покрытие.

Более того, если ваш действительный метод теста PHPUnit не содержит никаких утверждений (и вы вынуждены использовать @doesNotPerformAssertions аннотации от PHPUnit), покрытие для такого теста также не генерируется.

Другими словами, если вы заботитесь о покрытии кода, перенесите вещи в тесты PHPUnit с утверждениями.

0