wake-up-neo.com

Erreichen einer 100% Code-Abdeckung mit PHPUnit

Ich war gerade dabei, eine Testsuite für ein Projekt zu erstellen, und obwohl mir klar ist, dass eine 100% ige Abdeckung nicht die Metrik ist, nach der man streben sollte, enthält der Bericht über die Codeabdeckung ein seltsames Stück Ich hätte gerne eine Klarstellung.

Siehe Screenshot:

enter image description here

Da es sich bei der letzten Zeile der getesteten Methode um eine return handelt, wird die letzte Zeile (die nur eine schließende Klammer ist) als nie ausgeführt angezeigt. Infolgedessen wird die gesamte Methode in der Übersicht als nicht ausgeführt markiert. (Entweder das, oder ich lese den Bericht nicht richtig.)

Die vollständige Methode:

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];
}

Gibt es einen Grund dafür oder ist es eine Panne?

(Ja, ich lese durch Wie bekomme ich 100% Code Coverage mit PHPUnit , anderer Fall, obwohl ähnlich.)

Bearbeiten:

Beim Durchlaufen des Berichts stellte ich fest, dass dies auch für eine switch-Anweisung an einer anderen Stelle im Code gilt. So ist dieses Verhalten zumindest teilweise konsistent, aber dennoch verwirrend für mich.

Edit2:

Ich arbeite unter: PHPUnit 3.6.7, PHP 5.4.0RC5, XDebug 2.2.0-dev unter OS X

35
nikc.org

Zunächst einmal: 100% Codeabdeckung ist eine großartige Metrik füranstreben. Es ist einfach nicht immer mit einem vernünftigen Aufwand erreichbar und es ist nicht immer wichtig, dies zu tun :)

Das Problem stammt von xDebug, das PHPUnit mitteilt, dass diese Zeile ausführbar ist, aber nicht behandelt wird.

In einfachen Fällen kann xDebug feststellen, dass die Leitung NICHT erreichbar ist, sodass Sie dort eine 100% ige Codeabdeckung erhalten.

Siehe das einfache Beispiel unten.


2. Aktualisierung

Das Problem ist jetzt behoben xDebug bugtracker Wenn Sie eine neue Version von xDebug erstellen, werden diese Probleme behoben :)

Update (siehe unten für Probleme mit PHP 5.3.x)

Da Sie PHP 5.4 und die DEV-Version von xDebug ausführen, habe ich diese installiert und getestet. Ich habe die gleichen Probleme wie Sie mit der gleichen Ausgabe, die Sie kommentiert haben.

Ich bin mir nicht zu 100% sicher, ob das Problem von php-code-coverage (dem phpunit-Modul) für xDebug stammt. Dies könnte auch ein Problem mit xDebug dev sein.

Ich habe einen Fehler mit php-code-coverage eingereicht und wir werden herausfinden, wo das Problem herkommt.


Für PHP 5.3.x Probleme:

Für komplexere Fälle kannfehlschlagen.

Für den Code, den Sie gezeigt haben, kann ich nur sagen: "Es funktioniert für mich" ( komplexes Beispiel unten ).

Möglicherweise aktualisieren Sie die xDebug- und PHPUnit-Versionen und versuchen es erneut.

Ich habe gesehen, dass es mit aktuellen Versionen fehlgeschlagen ist, aber es hängt davon ab, wie die ganze Klasse manchmal aussieht.

Das Entfernen von ?: -Operatoren und anderen einzeiligen Dingen mit mehreren Anweisungen kann ebenfalls hilfreich sein.

Soweit mir bekannt ist, wird xDebug derzeit überarbeitet, um mehr dieser Fälle zu vermeiden. xDebug möchte einmal in der Lage sein, "Statement Coverage" bereitzustellen, und das sollte viele dieser Fälle beheben. Im Moment kann man hier nicht viel machen

Während //@codeCoverageIgnoreStart und //@codeCoverageIgnoreEnd diese Zeile "verdecken", sieht es wirklich hässlich aus und tut normalerweise mehr schlecht als gut.

Für einen anderen Fall, in dem dies passiert, lesen Sie die Fragen und Antworten von:

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


Einfaches Beispiel:

<?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;
    }
}

produziert:

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)

Komplexes Beispiel:

<?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 {}

produziert:

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
edorian

Das Hauptproblem hierbei ist, dass darauf bestanden wird, dass "Zeilen" zu 100% ausgeführt werden. (Manager mögen diese Idee; es ist ein einfaches Modell, das sie verstehen können). Viele Zeilen sind nicht "ausführbar" (Leerzeichen, Lücken zwischen Funktionsdeklarationen, Kommentaren, Deklarationen, "reine Syntax", z. B. das Schließen "}" einer Schalter- oder Klassendeklaration oder komplexe Anweisungen, die auf mehrere Quellzeilen aufgeteilt sind).

Was Sie wirklich wissen möchten, ist: "Ist der gesamte ausführbare Code abgedeckt?" Diese Unterscheidung scheint albern, führt aber zu einer Lösung. XDebug verfolgt, was ausgeführt wird, und zwar nach Zeilennummer, und Ihr XDebug-basiertes Schema meldet somit Bereiche ausgeführter Zeilen. Und Sie bekommen die Probleme, die in diesem Thread besprochen werden, einschließlich der klobigen Lösungen, den Code mit "Zählen Sie mich nicht" -Kommentaren zu versehen und "}" in die gleiche Zeile wie die letzte ausführbare Anweisung zu setzen, usw. Kein Programmierer ist das wirklich bereit, das zu tun, geschweige denn, es aufrechtzuerhalten.

Wenn man ausführbaren Code als den Code definiert, der aufgerufen werden kann oder von einer Bedingung gesteuert wird (was die Compiler als "Basisblöcke" bezeichnen) und das Coverage-Tracking auf diese Weise erfolgt, dann das Layout des Codes und die albernen Fälle einfach verschwinden. Ein Testabdeckungstool dieses Typs erfasst das, was als "Zweigabdeckung" bezeichnet wird, und Sie können buchstäblich 100% "Zweigabdeckung" erhalten oder nicht, indem Sie den gesamten ausführbaren Code ausführen. Außerdem werden die lustigen Fälle aufgegriffen, in denen Sie eine Bedingung innerhalb einer Zeile (mit "x? Y: z") oder in denen Sie zwei konventionelle Anweisungen in einer Zeile haben (z. B.

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

Da XDebug Zeilenweise verfolgt, wird dies meiner Meinung nach als eine Aussage behandelt, und es wird als Abdeckung betrachtet, wenn die Steuerung die Zeile erreicht, obwohl tatsächlich 5 Teile zu testen sind.

Unser PHP Test Coverage Tool setzt diese Ideen um. Insbesondere wird verstanden, dass der Code nach einer return-Anweisung nicht ausführbar ist und dass Sie ihn nicht ausgeführt haben, wenn er nicht leer ist. Dadurch verschwindet das ursprüngliche Problem des OP. Keine Spiele mehr, um "echte" Deckungszahlen zu erhalten.

Wie bei allen Entscheidungen gibt es manchmal einen Nachteil. Unser Tool verfügt über eine Code-Instrument-Komponente, die nur unter Windows ausgeführt wird. instrumentierter PHP Code kann überall ausgeführt werden und die Verarbeitung/Anzeige erfolgt durch ein plattformunabhängiges Java-Programm. Für das OSX-System von OP ist dies möglicherweise umständlich. Der Instrumenter funktioniert problemlos auf NFS-fähigen Dateisystemen, sodass er den Instrumenter möglicherweise auf einem PC ausführen und seine OSX-Dateien instrumentieren kann.

Dieses spezielle Problem wurde von jemandem angesprochen, der versuchte, seine Deckungszahl zu erhöhen. Das Problem war meiner Meinung nach künstlich und kann geheilt werden, indem man die Künstlichkeit umgeht. Es gibt eine andere Möglichkeit, Ihre Zahlen zu erhöhen, ohne weitere Tests zu schreiben. Dabei wird doppelter Code gefunden und entfernt. Wenn Sie Duplikate entfernen, müssen Sie weniger Code testen, und beim Testen einer (nicht) Kopie in effects wird die (jetzt nicht vorhandene andere) Kopie getestet, sodass Sie leichter höhere Zahlen erhalten. Hier können Sie mehr darüber lesen.

4
Ira Baxter

Fügen Sie einfach einen "Standard" -Fall hinzu, der nichts bewirkt, und Sie erhalten die vollständige Abdeckung.

1
Lloyd Watkin

Folgendes tun, um eine 100% ige Abdeckung der switch-Anweisung zu erhalten:

Stellen Sie sicher, dass mindestens ein Test einen nicht vorhandenen Fall sendet.

Also, wenn Sie haben:

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

stellen Sie sicher, dass mindestens einer Ihrer Tests einen Namen sendet, der weder terry noch lucky noch gerard ist.

0
Ibrahim Lawal