Come eseguire l'output in CLI durante l'esecuzione dei test dell'unità PHP?


151

Quando eseguo un test PHPUnit, vorrei essere in grado di scaricare l'output in modo da poter eseguire il debug di una o due cose.

Ho provato quanto segue (simile all'esempio del manuale PHPUnit );

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

Con il seguente risultato:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Si noti che non è presente alcun output previsto.

Sto usando le versioni HEAD dei repository git dal 19 settembre 2011.

Uscita di 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

C'è qualcosa che sto facendo di sbagliato o è potenzialmente un bug di PHPUnit?


1
Dov'è il codice che chiama il testOutput()metodo?
Derrick Tucker,

Ci provi davvero disperatamente (echo, print, print_r, var_dump - in pratica è tutto "output"), normalmente non ho problemi a fare output dai test. Puoi verificare se il buffering dell'output è abilitato: php.net/manual/en/function.ob-get-level.php - E il modo più sicuro per "testare" con forza è lanciare un'eccezione BTW.
Hacre,

3
@DerrickTucker PHPUnit lo fa chiamando phpunit /path/to/tests/theTest.php(se la classe sopra fosse nel file theTest.php).
Jess Telford,

@hakre ob_get_level()ritorna 1. Tuttavia, questo è contraddetto dal seguente codice: con while (ob_get_level() > 0) { ob_end_flush(); }quali errori ob_end_clean(): failed to delete buffer. No buffer to delete.. Curioso e curioso.
Jess Telford,

1
Sta dicendo che è il codice di phpunit che sta innescando l'errore - ovviamente perché la deglutizione dell'output di phpunits è attiva (ma tu l'hai rotta). Guarda esattamente, anche il nome della funzione differisce.
Hacre,

Risposte:


196

AGGIORNARE

Ho appena realizzato un altro modo per farlo che funziona molto meglio --verbosedell'opzione della riga di comando:

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

Ciò consente di scaricare qualsiasi cosa sulla console in qualsiasi momento senza tutto l'output indesiderato fornito con l' --verboseopzione CLI.


Come hanno notato altre risposte, è meglio testare l'output usando i metodi integrati come:

$this->expectOutputString('foo');

Tuttavia, a volte è utile essere cattivi e vedere l'output di debug una tantum / temporaneo all'interno dei casi di test. Non è necessario per l' var_dumphack / soluzione alternativa, però. Ciò può essere facilmente ottenuto impostando l' --verboseopzione della riga di comando durante l'esecuzione della suite di test. Per esempio:

$ phpunit --verbose -c phpunit.xml

Ciò visualizzerà l'output dall'interno dei metodi di test durante l'esecuzione nell'ambiente CLI.

Vedere: Scrivere test per PHPUnit - Test dell'output .


5
scusate, mancate scriviamo a stderr. Funziona davvero. Sono stato costretto a usare file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);invece, perché avevo un messaggio Use of undefined constant STDERR - assumed 'STDERR'con fwrite .
Serge

Il problema è che questo non sembra funzionare con l'isolamento del processo.
donquixote,

@donquixote Non sorprende poiché il test verrà eseguito in un altro processo il cui output del flusso STDERR è probabilmente scartato ...
rdlowrey,

1
Puoi anche usare STDOUTinvece diSTERR
Chris

2
Sì. Funziona e sembra funzionare allo stesso modo di STDERR. Sto usando PHPUnit 4.5.0nella riga cmd di Windows. una echodichiarazione non dà gli stessi risultati. echoproduce ma solo dopo che viene visualizzato il risultato del test. fwrite(STDERR, 'string')o fwrite(STDOUT,'string')produrre gli stessi risultati: viene visualizzato un output prima del risultato del test.
Chris,

33

Aggiornamento: vedi l'aggiornamento di rdlowrey di seguito per quanto riguarda l'uso di fwrite(STDERR, print_r($myDebugVar, TRUE));come soluzione molto più semplice


Questo comportamento è intenzionale (come ha sottolineato Jasonbar ). Lo stato in conflitto del manuale è stato segnalato a PHPUnit.

Una soluzione alternativa consiste nel far sì che PHPUnit asserisca che l'output previsto è vuoto (quando in effetti è presente output) che attiverà la visualizzazione dell'output imprevisto.

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

dà:

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.

Assicurati di disabilitare tutte le altre asserzioni che hai per il test in quanto potrebbero non riuscire prima di testare l'asserzione di output (e quindi non vedrai l'output).


33

Prova a usare --debug

Utile se stai cercando di ottenere il percorso giusto per un file di dati di inclusione o di origine.


2
Questa è la risposta corretta per me. Tutte le dichiarazioni in fwrite scritte nelle risposte precedenti non hanno funzionato per me.
Kim Stacks il

9

Non è un bug, ma molto intenzionale. La soluzione migliore è scrivere in un file di registro di qualche tipo e mettere in coda il registro per controllare l'output.

Se si sta tentando di uscita di test, controllare questo fuori.

Anche:

Nota : si noti che PHPUnit ingoia tutto l'output emesso durante l'esecuzione di un test. In modalità rigorosa, un test che emette output non riuscirà.


1
Se fosse intenzionale, sicuramente il manuale non ne darebbe un esempio ? Inoltre, non provando a testare l'output stesso. Usandolo solo per osservare alcuni risultati, i test falliscono quando non dovrebbero.
Jess Telford,

Come scritto: normalmente non ho problemi a echeggiare durante l'esecuzione dei test. Potresti avere una configurazione che sta rilevando l'input.
Hacre,

1
Se non fosse intenzionale, sicuramente il manuale non direbbe che lo fosse .
Jasonbar,

1
Quindi sembra un conflitto nella documentazione. @hakre sembra avere la stessa impressione che avevo (che non dovrebbe essere ingoiato) - quale parte della documentazione è corretta?
Jess Telford,

I test di generazione dell'output falliscono SOLO quando --disallow-test-output (o il file conf è beStrictAboutOutputDuringTests = "true") - la documentazione ora dice "Un test che emette output, ad esempio invocando la stampa nel codice test o nel codice test, sarà contrassegnato come rischioso quando questo controllo è abilitato. " phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
Puntatore NULL

7

Sto avendo un po 'di fortuna con VisualPHPUnit , e mostra utile l'output, tra le altre cose.

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

Risultati TestHello


Hmm, perché il downvote? In che modo questo non è utile come metodo alternativo per scaricare l'output di debug in un test PHPUnit?
Bob Stein,

1
Sto indovinando questo è downvoted perché se qualcuno tenta di eseguire questo si otterrà un errore di sintassi. Un enorme.
Jimbo,

D'oh ho dimenticato la funzione. Ora è riparato, testato, tagliato e incollato. Grazie, @Jimbo
Bob Stein,

Purtroppo al momento non è compatibile con PHP 7, apparentemente: "VisualPHPUnit non è attualmente compatibile con php 7 a causa del modo in cui viene utilizzata phpunit. Php 7 sarà supportato nella prossima versione principale"
leo

6

Dovresti davvero pensare alle tue intenzioni: se hai bisogno delle informazioni ora durante il debug per correggere il test, ti serviranno di nuovo la prossima settimana quando i test si interrompono.

Questo significa che avrai bisogno delle informazioni sempre quando il test fallisce - e l'aggiunta di un var_dumpper trovare la causa è semplicemente troppo lavoro. Piuttosto metti i dati nelle tue affermazioni.

Se il tuo codice è troppo complesso per quello, suddividilo fino a raggiungere un livello in cui un'asserzione (con un messaggio personalizzato) ti dice abbastanza per sapere dove si è rotto, perché e come risolvere il codice.


1
Sono d'accordo al 100% con tutto ciò che hai detto. Sto usando PHPUnit per fare test di integrazione che alla fine chiamano una delle API XML di Google. Tutti i test unitari sono stati superati (con chiamate API derise), ma il test finale (con chiamate API live) non è riuscito. Si è scoperto che era colpa dell'API di Google, ma nel frattempo volevo scaricare la risposta HTTP non elaborata.
Jess Telford,

2
Cosa succede se è necessario eseguire il debug del codice sulla strada per ottenere ciò che è stato delineato qui?
David Meister,

2
Questo è il motivo per cui non mi piacciono le risposte che indovinano ciò che gli utenti vogliono fare. Sono qui perché ho dei test che attendono la cancellazione della cache. Con 5 secondi di cache ttls, ciò significa che il mio test sembra bloccarsi per ~ 16 secondi. Vorrei solo avvisare l'utente che no, non c'è niente di sbagliato, stiamo solo aspettando il timeout della cache. Se le persone possono semplicemente rispondere alla domanda, anche le persone con altri casi d'uso avrebbero la loro risposta.
user151841

4

In laravel 5 puoi usare dump (), scaricare il contenuto dall'ultima risposta.

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


4

Basta usare il flag --verbose quando si esegue phpunit .

$ phpunit --verbose -c phpunit.xml 

Il vantaggio di questo metodo è che non è necessario modificare il codice di prova, è possibile stampare stringhe, var_dump o qualsiasi cosa si desideri sempre e verrà visualizzata nella console solo quando è impostata la modalità dettagliata .

Spero che aiuti.


3

In alcuni casi si potrebbe usare qualcosa del genere per inviare qualcosa alla console

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();
    }
}

3

Hackish, ma funziona: genera un'eccezione con l'output di debug come messaggio.

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

I rendimenti:

...
There was 1 error:

1) theTest::testOutput
Exception: hello

2

Questo è stato preso da PHPUnit Docs sulle partite .

Ciò dovrebbe consentire di scaricare informazioni in qualsiasi momento durante il ciclo di vita del test phpunit.

Sostituisci __METHOD__il codice qui sotto con quello che vuoi produrre

Esempio 4.2: Esempio che mostra tutti i metodi modello disponibili

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

1

Ho generato il mio HTML di Testresults, in questo caso è stato utile svuotare il contenuto:

var_dump($array);
ob_flush();

Esiste un secondo metodo PHP

flush() 

che non ho provato.


1

PHPUnit nasconde l'output con ob_start(). Possiamo disabilitarlo temporaneamente.

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

0

Ho dovuto modificare il codice sorgente per far funzionare questo codice, quindi è necessario aggiungere l'URL per questi repository biforcati al compositore perché funzionerà

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

0

Ecco alcuni metodi utili per stampare messaggi di debug in PHPUnit 4.x:

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

    Esempio più pratico:

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

    La chiamata syslog()genererà un messaggio di registro di sistema (vedere:) man syslog.conf.

    Nota: i livelli possibili: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, etc.

    Su macOS, per eseguire lo streaming dei messaggi syslog in tempo reale, eseguire:

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

    Nota: la STDERRcostante non è disponibile se si legge lo script PHP da stdin . Ecco la soluzione alternativa .

    Nota: invece di STDERR, puoi anche specificare un nome file.

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

    Nota: utilizzare questo metodo, se non si ha una STDERRcostante definita.

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

    Nota: utilizzare questo metodo se si desidera stampare qualcosa alla fine senza influire sui test.

Per scaricare la variabile, utilizzare var_export(), ad es"Value: " . var_export($some_var, TRUE) . "\n" .

Per stampare i messaggi sopra solo durante la modalità dettagliata o di debug, vedi: C'è un modo per dire se --debug o --verbose è stato passato a PHPUnit in un test?


Tuttavia, se il test dell'output fa parte del test stesso, controllare: pagina Test dei documenti di output .


-1

Se si utilizza Laravel, è possibile utilizzare le funzioni di registrazione come info () per accedere al file di registro Laravel in memoria / registri. Quindi non apparirà nel tuo terminale ma nel file di registro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.