Wie in CLI während der Ausführung von PHP Unit Tests ausgeben?

151

Wenn ich einen PHPUnit-Test durchführe, möchte ich in der Lage sein, die Ausgabe zu sichern, damit ich ein oder zwei Dinge debuggen kann.

Ich habe Folgendes versucht (ähnlich dem Beispiel im PHPUnit-Handbuch );

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

Mit folgendem Ergebnis:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Beachten Sie, dass keine der erwarteten Ausgaben vorhanden ist.

Ich verwende die HEAD-Versionen der Git-Repos vom 19. September 2011.

Ausgabe von php -version:

$ php -version
PHP 5.2.9 (cli) (built: Dec  8 2010 11:36:37) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
    with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans

Gibt es irgendetwas, was ich falsch mache, oder ist dies möglicherweise ein PHPUnit-Fehler?

Jess Telford
quelle
1
Wo ist der Code, der die testOutput()Methode aufruft ?
Derrick Tucker
Sie versuchen es wirklich verzweifelt (echo, print, print_r, var_dump - es ist im Grunde alles "Ausgabe"), normalerweise habe ich kein Problem damit, die Ausgabe von Tests durchzuführen. Sie können überprüfen, ob die Ausgabepufferung aktiviert ist: php.net/manual/en/function.ob-get-level.php - Und der sicherste Weg, um zwangsweise zu "testen", besteht darin, eine Ausnahme BTW auszulösen.
hakre
3
@DerrickTucker PHPUnit ruft dazu auf phpunit /path/to/tests/theTest.php(wenn die obige Klasse in der Datei enthalten ist theTest.php).
Jess Telford
@akre ob_get_level()kehrt zurück 1. Dem widerspricht jedoch der folgende Code: while (ob_get_level() > 0) { ob_end_flush(); }welche Fehler mit ob_end_clean(): failed to delete buffer. No buffer to delete.. Neugieriger und neugieriger.
Jess Telford
1
Es heißt, dass es der Code von phpunit ist, der den Fehler auslöst - offensichtlich, weil das Schlucken der phpunits-Ausgabe aktiv ist (aber Sie haben ihn gebrochen). Schauen Sie genau hin, der Funktionsname unterscheidet sich ebenfalls.
hakre

Antworten:

195

AKTUALISIEREN

Ich habe gerade eine andere Möglichkeit erkannt, die viel besser funktioniert als die --verboseBefehlszeilenoption:

class TestSomething extends PHPUnit_Framework_TestCase {
    function testSomething() {
        $myDebugVar = array(1, 2, 3);
        fwrite(STDERR, print_r($myDebugVar, TRUE));
    }
}

Auf diese Weise können Sie jederzeit alles auf Ihre Konsole kopieren, ohne die unerwünschte Ausgabe, die mit der --verboseCLI-Option einhergeht.


Wie andere Antworten festgestellt haben, ist es am besten, die Ausgabe mit den integrierten Methoden wie den folgenden zu testen:

$this->expectOutputString('foo');

Manchmal ist es jedoch hilfreich, ungezogen zu sein und einmalige / vorübergehende Debugging-Ausgaben in Ihren Testfällen zu sehen. Der var_dumpHack / Workaround ist jedoch nicht erforderlich . Dies kann leicht erreicht werden, indem Sie die --verboseBefehlszeilenoption festlegen, wenn Sie Ihre Testsuite ausführen. Beispielsweise:

$ phpunit --verbose -c phpunit.xml

Dadurch wird die Ausgabe innerhalb Ihrer Testmethoden angezeigt, wenn Sie in der CLI-Umgebung ausgeführt werden.

Siehe: Schreiben von Tests für PHPUnit - Testen der Ausgabe .

rdlowrey
quelle
5
sorry, verpasst wir schreiben an stderr. In der Tat funktioniert. Ich war nur gezwungen, file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);stattdessen zu verwenden , weil ich eine Nachricht Use of undefined constant STDERR - assumed 'STDERR'mit fwrite hatte .
Serge
Das Problem ist, dass dies mit der Prozessisolation nicht zu funktionieren scheint.
Donquijote
@donquixote Kein Wunder, da der Test in einem anderen Prozess ausgeführt wird, dessen STDERR-Stream-Ausgabe wahrscheinlich verworfen wird ...
Rdlowrey
1
Sie können auch STDOUTanstelle vonSTERR
Chris
2
Ja. Es funktioniert und scheint genauso auszugeben wie STDERR. Ich benutze PHPUnit 4.5.0in Windows Cmd Linie. Eine echoAussage liefert nicht die gleichen Ergebnisse. echowird ausgegeben, jedoch erst, nachdem das Testergebnis angezeigt wurde. fwrite(STDERR, 'string')oder fwrite(STDOUT,'string')dieselben Ergebnisse erzielen: Eine Ausgabe vor dem Testergebnis wird angezeigt.
Chris
33

Update: Siehe das Update von rdlowrey unten bezüglich der Verwendung fwrite(STDERR, print_r($myDebugVar, TRUE));als viel einfachere Lösung


Dieses Verhalten ist beabsichtigt (wie jasonbar hat darauf hingewiesen ). Der widersprüchliche Status des Handbuchs wurde PHPUnit gemeldet .

Eine Problemumgehung besteht darin, dass PHPUnit bestätigt, dass die erwartete Ausgabe leer ist (wenn tatsächlich eine Ausgabe vorliegt), wodurch die unerwartete Ausgabe ausgelöst wird, die angezeigt wird.

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        $this->expectOutputString(''); // tell PHPUnit to expect '' as output
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

gibt:

PHPUnit @package_version@ by Sebastian Bergmann.

F

Time: 1 second, Memory: 3.50Mb

There was 1 failure:

1) theTest::testOutput
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-''
+'Hello WorldPingPongstring(4) "Foo"
+'

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Deaktivieren Sie unbedingt alle anderen Assertions, die Sie für den Test haben, da diese möglicherweise fehlschlagen, bevor die Output-Assertion getestet wird (und Sie daher die Ausgabe nicht sehen).

Jess Telford
quelle
33

Versuchen Sie es mit --debug

Nützlich, wenn Sie versuchen, den richtigen Pfad zu einer Include- oder Quelldatendatei zu finden.

chim
quelle
2
Das ist die richtige Antwort für mich. Alle in den früheren Antworten geschriebenen fwrite-Aussagen haben bei mir nicht funktioniert.
Kim Stacks
9

Es ist kein Fehler, aber sehr beabsichtigt. Am besten schreiben Sie in eine Protokolldatei und verfolgen das Protokoll, um die Ausgabe zu überwachen.

Wenn Sie versuchen, die Ausgabe zu testen, überprüfen Sie dies .

Ebenfalls:

Hinweis : Bitte beachten Sie, dass PHPUnit alle Ausgaben verschluckt, die während der Ausführung eines Tests ausgegeben werden. Im strengen Modus schlägt ein Test fehl, der eine Ausgabe ausgibt.

jasonbar
quelle
1
Wenn es beabsichtigt wäre, würde das Handbuch sicherlich kein Beispiel dafür geben ? Versuchen Sie auch nicht, die Ausgabe selbst zu testen. Verwenden Sie es nur, um einige Ergebnisse zu betrachten, die dazu führen, dass Tests fehlschlagen, wenn sie nicht sollten.
Jess Telford
Wie geschrieben: Normalerweise habe ich kein Problem, wenn Tests ausgeführt werden. Möglicherweise haben Sie eine Konfiguration, die Eingaben erfasst.
hakre
1
Wenn es nicht beabsichtigt wäre, würde das Handbuch sicherlich nicht sagen, dass es war .
Jason Bar
1
Es scheint also ein Konflikt in der Dokumentation zu sein. @hakre scheint den gleichen Eindruck zu haben, den ich hatte (dass es nicht verschluckt werden sollte) - welcher Teil der Dokumentation ist korrekt?
Jess Telford
Tests zum Generieren der Ausgabe schlagen NUR fehl, wenn --disallow-test-output (oder die conf-Datei beStrictAboutOutputDuringTests = "true" enthält) in der Dokumentation jetzt "Ein Test, der die Ausgabe ausgibt, z. B. durch Aufrufen von print im Testcode oder im getesteten Code". wird als riskant markiert, wenn diese Prüfung aktiviert ist. " phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
NULL Zeiger
7

Ich habe etwas Glück mit VisualPHPUnit und es zeigt unter anderem die Ausgabe hilfreich an.

class TestHello extends PHPUnit_Framework_TestCase 
{
    public function test_Hello() 
    {
        print "hello world";
    }
}

TestHello Ergebnisse

Bob Stein
quelle
Hmm, warum das Downvote? Wie ist dies nicht hilfreich als alternative Möglichkeit, die Debug-Ausgabe in einem PHPUnit-Test zu sichern?
Bob Stein
1
Ich vermute , dies ist , weil wenn jemand downvoted versucht diese laufen Sie einen Syntaxfehler erhalten. Ein massiver.
Jimbo
D'oh, ich habe die Funktion vergessen. Jetzt ist es fixiert, getestet, ausgeschnitten und eingefügt. Vielen Dank, @ Jimbo
Bob Stein
Leider ist es momentan nicht mit PHP 7 kompatibel, anscheinend: "VisualPHPUnit ist derzeit aufgrund der Art und Weise, wie phpunit verwendet wird, nicht mit PHP 7 kompatibel. Php 7 wird in der nächsten Hauptversion unterstützt"
Leo
6

Sie sollten wirklich über Ihre Absichten nachdenken: Wenn Sie die Informationen jetzt beim Debuggen benötigen, um den Test zu beheben, benötigen Sie sie nächste Woche erneut, wenn die Tests unterbrochen werden.

Dies bedeutet, dass Sie die Informationen immer dann benötigen, wenn der Test fehlschlägt - und das Hinzufügen eines var_dump, um die Ursache zu finden, ist einfach zu viel Arbeit. Setzen Sie die Daten lieber in Ihre Aussagen ein.

Wenn Ihr Code dafür zu komplex ist, teilen Sie ihn auf, bis Sie eine Ebene erreicht haben, in der eine Behauptung (mit einer benutzerdefinierten Nachricht) Ihnen genug sagt, um zu wissen, wo er gebrochen ist, warum und wie der Code repariert werden kann.

cweiske
quelle
1
Ich stimme zu 100% mit allem überein, was Sie gesagt haben. Ich verwende PHPUnit, um Integrationstests durchzuführen, die letztendlich eine der XML-APIs von Google aufrufen. Alle Unit-Tests bestanden (mit verspotteten API-Aufrufen), aber der letzte Test (mit Live-API-Aufrufen) ist fehlgeschlagen. Es stellte sich heraus, dass es die Schuld der Google-API war, aber in der Zwischenzeit wollte ich die rohe HTTP-Antwort sichern.
Jess Telford
2
Was ist, wenn Sie Ihren Code debuggen müssen, um das zu erreichen, was Sie hier beschrieben haben?
David Meister
2
Aus diesem Grund mag ich keine Antworten, die hinterfragen, was die Benutzer tun möchten. Ich bin hier, weil ich einen Test habe, der darauf wartet, dass der Cache geleert wird. Bei 5-Sekunden-Cache-TTLs scheint mein Test ~ 16 Sekunden lang zu hängen . Ich möchte dem Benutzer nur mitteilen, dass nein, nichts ist falsch, wir warten nur darauf, dass die Zeit zwischen den Caches abläuft. Wenn Menschen nur die Frage beantworten können, haben auch Menschen mit anderen Anwendungsfällen ihre Antwort.
user151841
4

In Laravel 5 können Sie dump () verwenden. Speichern Sie den Inhalt der letzten Antwort.

class ExampleTest extends TestCase{
    public function test1()
    {
        $this->post('/user', ['name' => 'Gema']);
        $this->dump();
    }
}

gibt

Branny Bk
quelle
4

Verwenden Sie einfach das Flag --verbose, wenn Sie phpunit ausführen .

$ phpunit --verbose -c phpunit.xml 

Der Vorteil dieser Methode ist, dass Sie den Testcode nicht ändern müssen, Sie können Zeichenfolgen drucken, var_dump's o alles, was Sie immer wünschen, und es wird in der Konsole nur angezeigt, wenn der ausführliche Modus eingestellt ist.

Ich hoffe das hilft.

Fabricio
quelle
3

In einigen Fällen könnte man so etwas verwenden, um etwas an die Konsole auszugeben

class yourTests extends PHPUnit_Framework_TestCase
{
    /* Add Warnings */
    protected function addWarning($msg, Exception $previous = null)
    {
        $add_warning = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_Warning($msg, 0, $previous);
        $add_warning->addWarning($this, $msg, time());
        $this->setTestResultObject($add_warning);
    }

    /* Add errors */
    protected function addError($msg, Exception $previous = null)
    {
        $add_error = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_error->addError($this, $msg, time());
        $this->setTestResultObject($add_error);
    }

    /* Add failures */
    protected function addFailure($msg, Exception $previous = null)
    {
        $add_failure = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_failure->addFailure($this, $msg, time());
        $this->setTestResultObject($add_failure);
    }

    public function test_messages()
    {
        $this->addWarning("Your warning message!");
        $this->addError("Your error message!");
        $this->addFailure("Your Failure message");
    }

    /* Or just mark test states! */
    public function test_testMarking()
    {
        $this->markTestIncomplete();
        $this->markTestSkipped();
    }
}
mkungla
quelle
3

Hackisch, funktioniert aber: Wirf eine Ausnahme mit der Debug-Ausgabe als Nachricht aus.

class theTest extends PHPUnit_Framework_TestCase
{
    public function testOutput() {
        throw new \Exception("hello");
    }   
}

Ausbeuten:

...
There was 1 error:

1) theTest::testOutput
Exception: hello
Matthias Rella
quelle
2

Dies wurde aus PHPUnit Docs about Fixtures übernommen .

Dies sollte es Ihnen ermöglichen, Informationen zu jedem Zeitpunkt während des Lebenszyklus des phpunit-Tests zu sichern.

Ersetzen __METHOD__Sie einfach den folgenden Code durch das, was Sie ausgeben möchten

Beispiel 4.2: Beispiel mit allen verfügbaren Vorlagenmethoden

<?php
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
    public static function setUpBeforeClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function setUp()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function assertPreConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function testOne()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(TRUE);
    }

    public function testTwo()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(FALSE);
    }

    protected function assertPostConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function tearDown()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public static function tearDownAfterClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function onNotSuccessfulTest(Exception $e)
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        throw $e;
    }
}
?>
Chris
quelle
1

Ich gebe meine Testergebnisse HTML-basiert aus. In diesem Fall war es hilfreich, den Inhalt zu leeren:

var_dump($array);
ob_flush();

Es gibt eine zweite PHP-Methode

flush() 

was ich nicht versucht habe.

Sudo
quelle
1

PHPUnit versteckt die Ausgabe mit ob_start(). Wir können es vorübergehend deaktivieren.

    public function log($something = null)
    {
        ob_end_clean();
        var_dump($something);
        ob_start();
    }
Slawa
quelle
0

Ich musste den Quellcode ändern, damit dieser Code funktioniert, sodass Sie dem Composer eine URL für diese gegabelten Repos hinzufügen müssen, damit dies funktioniert

class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     *  Save last response
     * @var Response|null A Response instance
     */
    static $lastResponse;
    /**
     *  Modify to save response
     *
     * @param  string $method
     * @param  string $uri
     * @param  array $parameters
     * @param  array $files
     * @param  array $server
     * @param  string $content
     * @param  bool $changeHistory
     * @return \Illuminate\Http\Response
     */
    final public function call(
        $method,
        $uri,
        $parameters = [],
        $files = [],
        $server = [],
        $content = null,
        $changeHistory = true
    ) {

        $response = parent::call($method, $uri, $parameters, $files, $server, $content, $changeHistory);
        static::$lastResponse = $this->client->getResponse();
        return $response;
    }


    /**
     * Modify message to add response text
     *
     * @param mixed $value
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string $message
     * @since  Method available since Release 3.0.0
     */
    final public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
    {
        $message .= PHP_EOL . static::$lastResponse . PHP_EOL;
        parent::assertThat($value, $constraint, $message);
    }
}
Gadelkareem
quelle
0

Hier sind einige Methoden zum Drucken von Debug-Meldungen in PHPUnit 4.x:

  • syslog(LOG_DEBUG, "Debug: Message 1!");

    Praktischeres Beispiel:

    syslog(LOG_DEBUG, sprintf("%s: Value: %s", __METHOD__, var_export($_GET, TRUE)));

    Beim Aufruf syslog()wird eine Systemprotokollnachricht generiert (siehe :) man syslog.conf.

    Hinweis: Mögliche Ebene: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, usw.

    Führen Sie unter macOS Folgendes aus, um die Syslog-Nachrichten in Echtzeit zu streamen:

    log stream --level debug --predicate 'processImagePath contains "php"'
  • fwrite(STDERR, "LOG: Message 2!\n");

    Hinweis: Die STDERRKonstante ist nicht verfügbar, wenn Sie das PHP-Skript von stdin lesen . Hier ist die Problemumgehung .

    Hinweis: Stattdessen STDERRkönnen Sie auch einen Dateinamen angeben.

  • file_put_contents('php://stderr', "LOG: Message 3!\n", FILE_APPEND);

    Hinweis: Verwenden Sie diese Methode, wenn Sie keine STDERRKonstante definiert haben.

  • register_shutdown_function('file_put_contents', 'php://stderr', "LOG: Message 4!\n", FILE_APPEND);

    Hinweis: Verwenden Sie diese Methode, wenn Sie ganz am Ende etwas drucken möchten, ohne die Tests zu beeinträchtigen.

Verwenden Sie zum Speichern der Variablen var_export()z "Value: " . var_export($some_var, TRUE) . "\n".

Informationen zum Drucken der obigen Nachrichten nur im ausführlichen oder Debug-Modus finden Sie unter: Gibt es eine Möglichkeit, festzustellen, ob --debug oder --verbose in einem Test an PHPUnit übergeben wurde?


Wenn das Testen der Ausgabe Teil des Selbsttests ist, lesen Sie Folgendes : Testen der Seite " Ausgabedokumente testen" .

Kenorb
quelle
-1

Wenn Sie Laravel verwenden, können Sie Protokollierungsfunktionen wie info () verwenden, um sich in der Laravel-Protokolldatei unter storage / logs anzumelden. Es wird also nicht in Ihrem Terminal, sondern in der Protokolldatei angezeigt.

Löwe
quelle