Достижение 100% покрытия кода с помощью PHPUnit - PullRequest
35 голосов
/ 10 января 2012

Я был в процессе создания набора тестов для проекта, и хотя я понимаю, что получение 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, XDebug2.2.0-dev на OS X

Ответы [ 4 ]

36 голосов
/ 10 января 2012

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

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

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

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


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

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

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

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

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

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


Для вопросов PHP 5.3.x:

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

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

Возможно, обновите версии 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)
4 голосов
/ 04 февраля 2012

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

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

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

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

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

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

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

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

1 голос
/ 04 февраля 2012

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

0 голосов
/ 09 мая 2017

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

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

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

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

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

...