Comment sortir en CLI lors de l'exécution des tests unitaires PHP?

151

Lors de l'exécution d'un test PHPUnit, je voudrais pouvoir vider la sortie afin de pouvoir déboguer une ou deux choses.

J'ai essayé ce qui suit (similaire à l' exemple du manuel 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);
    }   
}

Avec le résultat suivant:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Notez qu'il n'y a aucune sortie attendue.

J'utilise les versions HEAD des dépôts git en date du 19 septembre 2011.

Sortie de 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

Y a-t-il quelque chose que je fais de mal, ou est-ce potentiellement un bogue PHPUnit?

Jess Telford
la source
1
Où est le code qui appelle la testOutput()méthode?
Derrick Tucker le
Vous essayez vraiment désespérément (echo, print, print_r, var_dump - c'est fondamentalement tout "sortie"), normalement je n'ai pas de problème à faire la sortie des tests. Vous pouvez vérifier si la mise en mémoire tampon de sortie est activée: php.net/manual/en/function.ob-get-level.php - Et le moyen le plus sûr de "tester" avec force est de lancer une exception BTW.
hakre
3
@DerrickTucker PHPUnit fait cela en appelant phpunit /path/to/tests/theTest.php(si la classe ci-dessus était dans le fichier theTest.php).
Jess Telford
@hakre ob_get_level()revient 1. Cependant, cela est contredit par le code suivant: avec while (ob_get_level() > 0) { ob_end_flush(); }quelles erreurs ob_end_clean(): failed to delete buffer. No buffer to delete.. Curieux et curieux.
Jess Telford
1
Cela dit que c'est le code de phpunit qui déclenche l'erreur - évidemment parce que la déglutition de la sortie de phpunits est active (mais vous l'avez cassée). Regardez précisément, le nom de la fonction diffère également.
hakre

Réponses:

196

METTRE À JOUR

Je viens de réaliser une autre façon de faire cela qui fonctionne beaucoup mieux que l' --verboseoption de ligne de commande:

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

Cela vous permet de vider n'importe quoi sur votre console à tout moment sans toute la sortie indésirable fournie avec l' --verboseoption CLI.


Comme d'autres réponses l'ont noté, il est préférable de tester la sortie en utilisant les méthodes intégrées telles que:

$this->expectOutputString('foo');

Cependant, il est parfois utile d'être méchant et de voir une sortie de débogage ponctuelle / temporaire à partir de vos cas de test. Il n'y a pas besoin de var_dumphack / solution de contournement, cependant. Cela peut facilement être accompli en définissant l' --verboseoption de ligne de commande lors de l'exécution de votre suite de tests. Par exemple:

$ phpunit --verbose -c phpunit.xml

Cela affichera la sortie de vos méthodes de test lors de l'exécution dans l'environnement CLI.

Voir: Ecrire des tests pour PHPUnit - Tester la sortie .

rdlowrey
la source
5
désolé, nous avons manqué d'écrire à stderr. Fonctionne en effet. J'ai juste été obligé d'utiliser à la file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);place, car j'avais un message Use of undefined constant STDERR - assumed 'STDERR'avec fwrite .
Serge
Le problème est que cela ne semble pas fonctionner avec l'isolation des processus.
donquixote le
@donquixote Pas surprenant car le test s'exécutera dans un autre processus dont la sortie du flux STDERR est probablement rejetée ...
rdlowrey
1
Vous pouvez également utiliser à la STDOUTplace deSTERR
Chris
2
Oui. Cela fonctionne et semble sortir de la même manière que STDERR. J'utilise PHPUnit 4.5.0dans la ligne cmd de Windows. une echodéclaration ne donne pas les mêmes résultats. echosort mais seulement après l'affichage du résultat du test. fwrite(STDERR, 'string')ou fwrite(STDOUT,'string')produire les mêmes résultats: Une sortie avant l'affichage du résultat du test.
Chris
33

Mise à jour: voir la mise à jour de rdlowrey ci-dessous concernant l'utilisation de fwrite(STDERR, print_r($myDebugVar, TRUE));comme un travail beaucoup plus simple


Ce comportement est intentionnel (comme jasonbar l' a souligné ). L'état contradictoire du manuel a été signalé à PHPUnit.

Une solution de contournement consiste à demander à PHPUnit d'affirmer que la sortie attendue est vide (en cas de sortie), ce qui déclenchera l'affichage de la sortie inattendue.

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

donne:

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.

Assurez-vous de désactiver toutes les autres assertions que vous avez pour le test car elles peuvent échouer avant que l'assertion de sortie ne soit testée (et par conséquent, vous ne verrez pas la sortie).

Jess Telford
la source
33

Essayez d'utiliser --debug

Utile si vous essayez d'obtenir le bon chemin vers un fichier de données d'inclusion ou de source.

chim
la source
2
C'est la bonne réponse pour moi. Toutes les déclarations fwrite écrites dans les réponses précédentes n'ont pas fonctionné pour moi.
Kim Stacks
9

Ce n'est pas un bug, mais très intentionnel. Votre meilleur pari est d'écrire dans un fichier journal quelconque et de suivre le journal pour surveiller la sortie.

Si vous essayez de tester la sortie, vérifiez ceci .

Aussi:

Remarque : veuillez noter que PHPUnit avale toutes les sorties émises lors de l'exécution d'un test. En mode strict, un test qui émet une sortie échouera.

Jasonbar
la source
1
Si c'était intentionnel, alors le manuel n'en donnerait sûrement pas un exemple ? Aussi, n'essayez pas de tester la sortie elle-même. Il suffit de l'utiliser pour observer certains résultats, ce qui fait échouer les tests alors qu'ils ne le devraient pas.
Jess Telford
Comme écrit: je n'ai normalement pas de problème à faire écho lorsque les tests sont exécutés. Vous avez peut-être une configuration qui capte des entrées.
hakre
1
Si ce n'était pas intentionnel, alors le manuel ne le dirait sûrement pas .
jasonbar
1
Cela semble donc un conflit dans la documentation. @hakre semble avoir la même impression que moi (qu'il ne faut pas l'avaler) - quelle partie de la documentation est correcte?
Jess Telford
Les tests de génération de sortie échouent UNIQUEMENT lorsque --disallow-test-output (ou le fichier conf a beStrictAboutOutputDuringTests = "true") - la documentation dit maintenant "Un test qui émet une sortie, par exemple en appelant print dans le code de test ou le code testé, sera marqué comme risqué lorsque cette vérification est activée. " phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
Pointeur NULL
7

J'ai de la chance avec VisualPHPUnit , et il affiche utilement la sortie, entre autres.

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

Résultats TestHello

Bob Stein
la source
Hmm, pourquoi le vote négatif? En quoi cela n'est-il pas utile comme moyen alternatif de vider la sortie de débogage dans un test PHPUnit?
Bob Stein
1
Je suppose que c'est un vote négatif, car si quelqu'un essaie de l'exécuter, vous obtiendrez une erreur de syntaxe. Un énorme.
Jimbo
D'oh j'ai oublié la fonction. Maintenant, il est corrigé, testé, coupé et collé. Merci, @Jimbo
Bob Stein
Malheureusement, il n'est pas compatible avec PHP 7 pour le moment, apparemment: "VisualPHPUnit n'est pas compatible avec php 7 pour le moment en raison de la façon dont phpunit est utilisé. Php 7 sera pris en charge dans la prochaine version majeure"
leo
6

Vous devriez vraiment penser à vos intentions: si vous avez besoin des informations maintenant lors du débogage pour corriger le test, vous en aurez besoin la semaine prochaine lorsque les tests seront interrompus.

Cela signifie que vous aurez toujours besoin des informations lorsque le test échoue - et ajouter un var_dumppour trouver la cause est tout simplement trop de travail. Mettez plutôt les données dans vos affirmations.

Si votre code est trop complexe pour cela, divisez-le jusqu'à ce que vous atteigniez un niveau où une assertion (avec un message personnalisé) vous en dit assez pour savoir où elle s'est cassée, pourquoi et comment corriger le code.

cweiske
la source
1
Je suis entièrement d'accord avec tout ce que vous avez dit. J'utilise PHPUnit pour faire des tests d'intégration qui appellent finalement l'une des API XML de Google. Tous les tests unitaires ont réussi (avec des appels d'API simulés), mais le test final (avec des appels d'API en direct) a échoué. Il s'est avéré que c'était la faute de l'API Google, mais en attendant, je voulais vider la réponse HTTP brute.
Jess Telford
2
Et si vous avez besoin de déboguer votre code pour réaliser ce que vous avez décrit ici?
David Meister
2
C'est pourquoi je n'aime pas les réponses qui remettent en question ce que les utilisateurs veulent faire. Je suis ici parce que j'ai des tests qui attendent l'effacement du cache. Avec 5 secondes de cache ttls, cela signifie que mon test semble se bloquer pendant ~ 16 secondes. Je voudrais juste envoyer un avis à l'utilisateur que non, rien ne va pas, nous attendons juste que les caches expirent. Si les gens peuvent simplement répondre à la question, les personnes ayant d'autres cas d'utilisation auront également leur réponse.
user151841
4

Dans laravel 5, vous pouvez utiliser dump (), Dump le contenu de la dernière réponse.

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

donne

Branny Bk
la source
4

Utilisez simplement l' indicateur --verbose lors de l'exécution de phpunit .

$ phpunit --verbose -c phpunit.xml 

L'avantage de cette méthode est que vous n'avez pas besoin de changer le code de test, vous pouvez imprimer des chaînes, var_dump ou tout ce que vous souhaitez toujours et il ne sera affiché dans la console que lorsque le mode verbeux est activé.

J'espère que ça aide.

Fabricio
la source
3

Dans certains cas, on pourrait utiliser quelque chose comme ça pour sortir quelque chose sur la 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();
    }
}
mkungla
la source
3

Hackish, mais fonctionne: lancez une exception avec la sortie de débogage comme message.

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

Rendements:

...
There was 1 error:

1) theTest::testOutput
Exception: hello
Matthias Rella
la source
2

Ceci a été tiré de la documentation PHPUnit sur les fixtures .

Cela devrait vous permettre de vider les informations à tout moment pendant le cycle de vie du test phpunit.

Remplacez simplement __METHOD__le code ci-dessous par ce que vous voulez afficher

Exemple 4.2: Exemple montrant toutes les méthodes de modèle disponibles

<?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
la source
1

Je sortie mon Testresults basé sur HTML, dans ce cas, il était utile de vider le contenu:

var_dump($array);
ob_flush();

Il existe une deuxième méthode PHP

flush() 

ce que je n'ai pas essayé.

Sudo
la source
1

PHPUnit cache la sortie avec ob_start(). Nous pouvons le désactiver temporairement.

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

J'ai dû modifier le code source pour que ce code fonctionne, vous devez donc ajouter l'URL de ce dépôt fourchu au compositeur pour que cela fonctionne

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
la source
0

Voici quelques méthodes utiles pour imprimer des messages de débogage dans PHPUnit 4.x:

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

    Exemple plus pratique:

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

    L'appel syslog()générera un message de journal système (voir:) man syslog.conf.

    Note: Les niveaux possibles: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, etc.

    Sur macOS, pour diffuser les messages syslog en temps réel, exécutez:

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

    Remarque: La STDERRconstante n'est pas disponible si vous lisez le script PHP depuis stdin . Voici la solution de contournement .

    Remarque: au lieu de STDERR, vous pouvez également spécifier un nom de fichier.

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

    Remarque: utilisez cette méthode si vous n'avez pas défini de STDERRconstante .

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

    Remarque: utilisez cette méthode si vous souhaitez imprimer quelque chose à la toute fin sans affecter les tests.

Pour vider la variable, utilisez var_export(), par exemple "Value: " . var_export($some_var, TRUE) . "\n".

Pour imprimer les messages ci-dessus uniquement en mode verbeux ou débogage, voir: Existe - t-il un moyen de savoir si --debug ou --verbose a été transmis à PHPUnit dans un test?


Bien que si le test de la sortie fasse partie du test lui-même, consultez la page: Test des documents de sortie .

Kenorb
la source
-1

Si vous utilisez Laravel, vous pouvez utiliser des fonctions de journalisation telles que info () pour vous connecter au fichier journal Laravel sous stockage / journaux. Il n'apparaîtra donc pas dans votre terminal mais dans le fichier journal.

Leo
la source