¿Cómo generar en CLI durante la ejecución de pruebas unitarias PHP?

151

Al ejecutar una prueba PHPUnit, me gustaría poder volcar la salida para poder depurar una o dos cosas.

He intentado lo siguiente (similar al ejemplo del Manual 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 el siguiente resultado:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Observe que no hay ninguno de los resultados esperados.

Estoy usando las versiones HEAD de los repositorios git a partir del 19 de septiembre de 2011.

Salida 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

¿Hay algo que estoy haciendo mal o es potencialmente un error de PHPUnit?

Jess Telford
fuente
1
¿Dónde está el código que llama al testOutput()método?
Derrick Tucker
Intenta desesperadamente (echo, print, print_r, var_dump, es básicamente todo "salida"), normalmente no tengo problemas para hacer la salida de las pruebas. Puede verificar si el buffer de salida está habilitado: php.net/manual/en/function.ob-get-level.php - Y la forma más segura de "probar" con fuerza es lanzar una excepción por cierto.
Hakre
3
@DerrickTucker PHPUnit hace esto llamando phpunit /path/to/tests/theTest.php(si la clase anterior estaba en el archivo theTest.php).
Jess Telford
@hakre ob_get_level()regresa 1. Sin embargo, esto se contradice con el siguiente código: con while (ob_get_level() > 0) { ob_end_flush(); }qué errores ob_end_clean(): failed to delete buffer. No buffer to delete.. Más curioso y más curioso.
Jess Telford
1
Está diciendo que es el código de phpunit el que está provocando el error, obviamente porque la deglución de salida de phpunits está activa (pero la rompió). Mire con precisión, el nombre de la función también difiere.
Hakre

Respuestas:

196

ACTUALIZAR

Acabo de darme cuenta de otra forma de hacer esto que funciona mucho mejor que la --verboseopción de línea de comando:

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

Esto le permite volcar cualquier cosa a su consola en cualquier momento sin toda la salida no deseada que viene junto con la --verboseopción CLI.


Como han señalado otras respuestas, es mejor probar la salida utilizando los métodos integrados como:

$this->expectOutputString('foo');

Sin embargo, a veces es útil ser travieso y ver resultados de depuración únicos / temporales desde sus casos de prueba. Sin var_dumpembargo, no es necesario el hack / solución. Esto se puede lograr fácilmente configurando la --verboseopción de línea de comando al ejecutar su conjunto de pruebas. Por ejemplo:

$ phpunit --verbose -c phpunit.xml

Esto mostrará resultados desde dentro de sus métodos de prueba cuando se ejecute en el entorno CLI.

Ver: Escritura de pruebas para PHPUnit - Prueba de salida .

rdlowrey
fuente
55
perdón, perdimos escribimos a stderr. De hecho funciona. Simplemente me vi obligado a usarlo file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);, porque tenía un mensaje Use of undefined constant STDERR - assumed 'STDERR'con fwrite .
Serge
El problema es que esto no parece funcionar con el aislamiento del proceso.
donquixote
@donquixote No es sorprendente ya que la prueba se ejecutará en otro proceso cuya salida de flujo STDERR probablemente se descarte ...
rdlowrey
1
También puede usar en STDOUTlugar deSTERR
Chris
2
Si. Funciona y parece salir de la misma manera que STDERR. Estoy usando PHPUnit 4.5.0en la línea de cmd de windows. Una echodeclaración no da los mismos resultados. echogenera salida pero solo después de que se muestra el resultado de la prueba. fwrite(STDERR, 'string')o fwrite(STDOUT,'string')producir los mismos resultados: una salida antes de que se muestre el resultado de la prueba.
Chris
33

Actualización: consulte la actualización de rdlowrey a continuación sobre el uso de fwrite(STDERR, print_r($myDebugVar, TRUE));una solución mucho más simple


Este comportamiento es intencional (como ha señalado jasonbar ). El estado conflictivo del manual ha sido reportado a PHPUnit.

Una solución alternativa es hacer que PHPUnit afirme que la salida esperada está vacía (cuando, de hecho, hay salida), lo que provocará que se muestre la salida inesperada.

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

da:

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.

Asegúrese de deshabilitar cualquier otra aserción que tenga para la prueba, ya que pueden fallar antes de que se pruebe la aserción de salida (y, por lo tanto, no verá la salida).

Jess Telford
fuente
33

Intenta usar --debug

Es útil si está intentando obtener la ruta correcta para un archivo de datos de inclusión o fuente.

chim
fuente
2
Esta es la respuesta correcta para mí. Todas las declaraciones de fwrite escritas en las respuestas anteriores no funcionaron para mí.
Kim Stacks
9

No es un error, pero es muy intencional. Su mejor opción es escribir en un archivo de registro de algún tipo y seguir el registro para observar la salida.

Si está intentando PROBAR salida, verifique esto .

También:

Nota : Tenga en cuenta que PHPUnit se traga toda la salida que se emite durante la ejecución de una prueba. En modo estricto, una prueba que emite resultados fallará.

Jasonbar
fuente
1
Si fuera intencional, ¿seguramente el manual no daría un ejemplo de ello ? Además, no intenta probar la salida en sí. Simplemente utilizándolo para mirar algunos resultados que hacen que las pruebas fallen cuando no deberían.
Jess Telford
Como está escrito: normalmente no tengo un problema para hacer eco cuando se ejecutan las pruebas. Es posible que tenga alguna configuración que atrape la entrada.
Hakre
1
Si no fuera intencional, entonces seguramente el manual no diría que lo fue .
jasonbar
1
Entonces parece un conflicto en la documentación. @hakre parece tener la misma impresión que yo (que no debería tragarse): ¿qué parte de la documentación es correcta?
Jess Telford
Las pruebas de generación de resultados SOLAMENTE fallan cuando --disallow-test-output (o el archivo conf tiene beStrictAboutOutputDuringTests = "true") - la documentación ahora dice "Una prueba que emite resultados, por ejemplo, al invocar print en el código de prueba o en el código probado, se marcará como arriesgado cuando esta verificación esté habilitada ". phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
Puntero NULL el
7

Estoy teniendo suerte con VisualPHPUnit , y muestra útilmente la salida, entre otras cosas.

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

TestHola resultados

Bob Stein
fuente
Hmm, ¿por qué el voto negativo? ¿Cómo no es esto útil como una forma alternativa de volcar la salida de depuración en una prueba PHPUnit?
Bob Stein
1
Estoy adivinando esto se downvoted porque si cualquiera intenta ejecutar este obtendrá un error de sintaxis. Una masiva.
Jimbo
Oh, olvidé la función. Ahora está arreglado, probado, cortado y pegado. Gracias, @Jimbo
Bob Stein
Lamentablemente, no es compatible con PHP 7 en este momento, aparentemente: "VisualPHPUnit no es compatible con php 7 en este momento debido a la forma en que se utiliza phpunit. Php 7 será compatible con la próxima versión principal"
leo
6

Realmente debería pensar en sus intenciones: si necesita la información ahora al depurar para corregir la prueba, la necesitará la próxima semana nuevamente cuando se rompan las pruebas.

Esto significa que usted va a necesitar la información siempre cuando la prueba falla - y añadiendo una var_dumppara encontrar la causa es demasiado trabajo. Más bien ponga los datos en sus afirmaciones.

Si su código es demasiado complejo para eso, divídalo hasta llegar a un nivel en el que una afirmación (con un mensaje personalizado) le diga lo suficiente como para saber dónde se rompió, por qué y cómo solucionar el código.

Cweiske
fuente
1
Estoy 100% de acuerdo con todo lo que dijiste. Estoy usando PHPUnit para hacer pruebas de integración que finalmente llaman a una de las API XML de Google. Todas las pruebas unitarias pasaron (con las llamadas API simuladas), pero la prueba final (con llamadas API en vivo) falló. Resultó que fue culpa de la API de Google, pero mientras tanto, quería volcar la respuesta HTTP sin procesar.
Jess Telford
2
¿Qué sucede si necesita depurar su código en el camino para lograr lo que ha descrito aquí?
David Meister
2
Es por eso que no me gustan las respuestas que adivinan qué quieren hacer los usuarios. Estoy aquí porque tengo una prueba que espera a que se borre el caché. Con ttls de caché de 5 segundos, eso significa que mi prueba parece bloquearse durante ~ 16 segundos. Solo me gustaría emitir un aviso al usuario de que no, nada está mal, solo estamos esperando que se agote el tiempo de espera de los cachés. Si las personas solo pueden responder la pregunta, entonces las personas con otros casos de uso también tendrían su respuesta.
user151841
4

En laravel 5 puede usar dump (), volcar el contenido de la última respuesta.

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

da

Branny Bk
fuente
4

Simplemente use la bandera --verbose cuando ejecute phpunit .

$ phpunit --verbose -c phpunit.xml 

La ventaja de este método es que no necesita cambiar el código de prueba, puede imprimir cadenas, var_dump o cualquier cosa que desee siempre y se mostrará en la consola solo cuando esté configurado el modo detallado .

Espero que esto ayude.

Fabricio
fuente
3

En algunos casos, se podría usar algo así para enviar algo a la consola

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
fuente
3

Hackish, pero funciona: produce una excepción con la salida de depuración como mensaje.

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

Rendimientos:

...
There was 1 error:

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

Esto fue tomado de PHPUnit Docs sobre Fixtures .

Esto debería permitirle volcar información en cualquier momento durante el ciclo de vida de la prueba phpunit.

Simplemente reemplace __METHOD__en el código a continuación con lo que quiera generar

Ejemplo 4.2: Ejemplo que muestra todos los métodos de plantilla 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
fuente
1

Emití mis Testresults basados ​​en HTML, en este caso fue útil para vaciar el contenido:

var_dump($array);
ob_flush();

Hay un segundo método PHP

flush() 

que no lo he intentado

Sudo
fuente
1

PHPUnit está ocultando la salida con ob_start(). Podemos deshabilitarlo temporalmente.

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

Tuve que modificar el código fuente para que este código funcione, por lo que debe agregar la URL de estos repositorios bifurcados al compositor para que esto funcione

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
fuente
0

Aquí hay algunos métodos útiles para imprimir mensajes de depuración en PHPUnit 4.x:

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

    Más ejemplo práctico:

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

    Las llamadas syslog()generarán un mensaje de registro del sistema (ver:) man syslog.conf.

    Nota: Los niveles posibles: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING,LOG_ERR , etc.

    En macOS, para transmitir los mensajes de syslog en tiempo real, ejecute:

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

    Nota: La STDERRconstante no está disponible si lee el script PHP desde stdin . Aquí está la solución .

    Nota: en lugar de STDERR, también puede especificar un nombre de archivo.

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

    Nota: Use este método, si no tiene una STDERRconstante definida.

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

    Nota: Use este método si desea imprimir algo al final sin afectar las pruebas.

Para volcar la variable, use var_export(), por ej."Value: " . var_export($some_var, TRUE) . "\n" .

Para imprimir los mensajes anteriores solo durante el modo detallado o de depuración, vea: ¿Hay alguna manera de saber si --debug o --verbose se pasó a PHPUnit en una prueba?


Aunque si la prueba de la salida es parte de la prueba en sí misma, consulte: Página Prueba de documentos de salida .

kenorb
fuente
-1

Si usa Laravel, puede usar funciones de registro como info () para iniciar sesión en el archivo de registro de Laravel en almacenamiento / registros. Por lo tanto, no aparecerá en su terminal sino en el archivo de registro.

León
fuente