Достижение 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 в другом месте кода. Так что такое поведение, по крайней мере, для некоторых степень последовательная, но тем не менее сбивающая меня с толку.

Правка2:

Я работаю на: PHPUnit 3.6.7, PHP 5.4.0RC5, xdebug 2.2.0-dev на OS X

Author: Community, 2012-01-10

4 answers

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

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

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

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


2-е обновление

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

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

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

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

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


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

Для более сложных случаев это МОЖЕТ не сработать.

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

Возможно, обновите версии 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)
 36
Author: edorian, 2017-05-23 10:31:19

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

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

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

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

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

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

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

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

 4
Author: Ira Baxter, 2012-02-05 21:43:55

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

 1
Author: Lloyd Watkin, 2012-02-04 15:51:16

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

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

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

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

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

 0
Author: Ibrahim Lawal, 2017-05-09 11:14:53