Подтвердить что ты не робот

Достижение 100% охвата кода с помощью PHPUnit

Я занимаюсь созданием набора тестов для проекта, и, хотя я понимаю, что 100% охват не является метрикой, к которой нужно стремиться, есть странный бит в отчете о покрытии кода, которому я хотелось бы получить некоторые разъяснения.

Смотрите снимок экрана:

enter image description here

Поскольку последняя строка тестируемого метода равна return, последняя строка (которая является только закрывающей скобкой) отображается как никогда не исполняемая, и, как следствие, весь метод помечен как не выполненный в Обзор. (Либо это, либо я не читаю отчет правильно.)

Полный метод:

static public function &getDomain($domain = null) {
    $domain = $domain ?: self::domain();

    if (! array_key_exists($domain, self::$domains)) {
        self::$domains[$domain] = new Config();
    }

    return self::$domains[$domain];
}

Есть ли причина для этого, или это сбой?

(Да, я прочитал Как получить 100% покрытие кода с помощью PHPUnit, в другом случае, хотя и аналогичный.)

Изменить:

Просматривая отчет, я заметил, что то же самое верно для оператора switch в другом месте кода. Таким образом, это поведение, по крайней мере, в некоторой степени непротиворечиво, но, тем не менее, озадачивает меня.

Edit2:

Я запускаю: PHPUnit 3.6.7, PHP 5.4.0RC5, XDebug 2.2.0-dev на OS X

4b9b3361

Ответ 1

Во-первых: 100% охват кода - это отличный показатель для strive. Это просто не всегда достижимо с разумным количеством усилий, и это не всегда важно для этого:)

Проблема возникает из xDebug, говорящего PHPUnit, что эта строка является исполняемой, но не покрывается.

Для простых случаев xDebug может сказать, что линия НЕ достижима, поэтому вы получаете покрытие на 100% кода.

См. ниже простой пример.


Второе обновление

Теперь проблема исправлена ​​ xDebug bugtracker, поэтому создание новой версии xDebug решит эти проблемы: )

Обновление (см. ниже проблемы с php 5.3.x)

Поскольку вы используете PHP 5.4 и DEV версию xDebug, я их установил и протестировал. Я сталкиваюсь с теми же проблемами, что и вы, с тем же выпуском, о котором вы прокомментировали.

Я не уверен на 100%, если проблема связана с php-code-coverage (модулем phpunit) для xDebug. Это также может быть проблемой с xDebug dev.

Я зарегистрировал ошибку с php-code-coverage, и мы выясним, откуда эта проблема.


Для проблем с PHP 5.3.x:

Для более сложных случаев эта CAN не работает.

Для кода, который вы показали, я могу сказать, что "Он работает для меня" (сложный образец ниже).

Возможно, обновите версии xDebug и PHPUnit и повторите попытку.

Я видел, как он терпит неудачу с текущими версиями, но зависит от того, как выглядит весь класс.

Удаление операторы ?: и другие однострочные многопозиционные вещи также могут помочь.

В xDebug продолжается рефакторинг, чтобы избежать большего числа случаев, насколько мне известно. xDebug однажды хочет иметь возможность "охвата заявлений", и это должно исправить многие из этих случаев. Пока что здесь мало что можно сделать.

Пока //@codeCoverageIgnoreStart и //@codeCoverageIgnoreEnd получат эту "крышу", она выглядит действительно уродливой и обычно делает хуже, чем хорошо.

В другом случае, когда это происходит, см. вопрос и ответы:

what-to-do-when-project-coding-standards-conflicts-with-unit-test-code-coverage


Простой пример:

<?php
class FooTest extends PHPUnit_Framework_TestCase {
    public function testBar() {
        $x = new Foo();
        $this->assertSame(1, $x->bar());
    }
}

<?php
class Foo {
    public function bar() {
        return 1;
    }
}

дает:

phpunit --coverage-text mep.php 
PHPUnit 3.6.7 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.50Mb

OK (1 test, 1 assertion)

Generating textual code coverage report, this may take a moment.

Code Coverage Report 
  2012-01-10 15:54:56

 Summary: 
  Classes: 100.00% (2/2)
  Methods: 100.00% (1/1)
  Lines:   100.00% (1/1)

Foo
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)

Комплексный пример:

<?php

require __DIR__ . '/foo.php';

class FooTest extends PHPUnit_Framework_TestCase {

    public function testBar() {
        $this->assertSame('b', Foo::getDomain('a'));
        $this->assertInstanceOf('Config', Foo::getDomain('foo'));
    }
}

<?php

class Foo {
    static $domains = array('a' => 'b');

    static public function &getDomain($domain = null) {
        $domain = $domain ?: self::domain();
        if (! array_key_exists($domain, self::$domains)) {
            self::$domains[$domain] = new Config();
        }
        return self::$domains[$domain];
    }
}

class Config {}

дает:

PHPUnit 3.6.7 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.50Mb

OK (1 test, 2 assertions)

Generating textual code coverage report, this may take a moment.

Code Coverage Report 
  2012-01-10 15:55:55

 Summary: 
  Classes: 100.00% (2/2)
  Methods: 100.00% (1/1)
  Lines:   100.00% (5/5)

Foo
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  5/  5)

Ответ 2

В значительной степени проблема заключается в настойчивости в получении 100% -ного охвата выполнения "строк". (Менеджеры подобны этой идее, это простая модель, которую они могут понять). Многие строки не являются "исполняемыми" (пробелы, пробелы между объявлениями функций, комментариями, декларациями, "чистым синтаксисом", например, закрытием "}" объявления коммутатора или класса или сложными операторами, разделенными на несколько строк исходного кода).

То, что вы действительно хотите знать, - это весь исполняемый код? Это различие кажется глупым, но приводит к решению. XDebug отслеживает то, что выполняется, ну, по номеру строки, и ваша схема на основе XDebug, таким образом, сообщает диапазоны выполненных строк. И вы получаете проблемы, обсуждаемые в этой теме, в том числе решения klunky, связанные с необходимостью комментировать код комментариями "не считайте меня", помещая "}" в ту же строку, что и последний исполняемый оператор, и т.д. На самом деле программист не является желая сделать это, не говоря уже о его сохранении.

Если один из них определяет исполняемый код как тот код, который может быть вызван или управляется условным (то, что люди-компиляторы называют "базовыми блоками" ), и отслеживание покрытия выполняется таким образом, тогда макет кода и глупые случаи просто исчезают. Инструмент тестового покрытия этого типа собирает то, что называется "охват веток", и вы можете получить или не получить 100% "охват веток" буквально, выполнив весь исполняемый код. Кроме того, он подберет те забавные случаи, когда у вас есть условие в строке (используя "x? Y: z" ) или у вас есть два обычных оператора в строке (например,

 if  (...)  {   if  (...)  stmt1; else stmt2; stmt3 }

Так как XDebug отслеживает по строкам, я верю, что он рассматривает это как один элемент и рассматривает его охват, если элемент управления попадает в строку, когда на самом деле есть 5 частей для фактического тестирования.

Наш инструмент проверки PHP Test Coverage реализует эти идеи. В частности, он понимает, что код, следующий за оператором return, не является исполняемым, и он скажет вам, что вы его не выполнили, если он не пуст. Это приводит к исчезновению исходной проблемы OP. Больше нет игр для получения "реальных" номеров покрытия.

Как и во всех вариантах, иногда есть недостаток. Наш инструмент имеет компонент инструмента кода, который работает только под Windows; инструментальный PHP-код может работать в любом месте, а обработка/отображение выполняется с помощью независимой от платформы Java-программы. Так что это может быть неудобно для системы OP OSX. Инструментарий отлично работает в файловых системах, совместимых с NFS, поэтому он мог бы, возможно, запустить прибор на ПК и обработать его файлы OSX.

Эта конкретная проблема была поднята кем-то, кто пытался подтолкнуть его номера покрытия; проблема была ИМХО искусственной и может быть излечена, обойдя искусственность. Там есть еще один способ увеличить число ваших номеров, не набирая больше тестов, а также обнаруживая и удаляя повторяющийся код. Если вы удаляете дубликаты, меньше кода для тестирования и тестирования одной (не) копии в тестах эффектов (теперь несуществующей другой копии), поэтому легче получить более высокие числа. Вы можете прочитать об этом здесь.

Ответ 3

Что касается проблемы покрытия кода оператора switch, просто добавьте случай "по умолчанию", который ничего не делает, и вы получите полное покрытие.

Ответ 4

Вот что нужно сделать, чтобы получить оператор switch 100%:

Убедитесь, что существует хотя бы один тест, который отправляет случай, который не существует.

Итак, если у вас есть:

switch ($name) {
    case 'terry':
        return 'blah';
    case 'lucky':
        return 'blahblah';
    case 'gerard':
        return 'blahblah';
}

убедитесь, что хотя бы одно из ваших тестов отправляет имя, которое не является ни terry, ни lucky, ни gerard.