Como produzir na CLI durante a execução de testes de unidade PHP?

151

Ao executar um teste PHPUnit, gostaria de poder despejar a saída para poder depurar uma ou duas coisas.

Eu tentei o seguinte (semelhante ao exemplo do Manual do 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);
    }   
}

Com o seguinte resultado:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Observe que não há saída esperada.

Estou usando as versões HEAD dos repositórios git a partir de 19 de setembro de 2011.

Saída 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

Existe algo que estou fazendo de errado ou isso é potencialmente um bug do PHPUnit?

Jess Telford
fonte
1
Onde está o código que chama o testOutput()método?
Derrick Tucker
Você tenta muito desesperadamente (eco, impressão, print_r, var_dump - é basicamente tudo "saída"), normalmente não tenho problemas para fazer a saída dos testes. Você pode verificar se o buffer de saída está ativado: php.net/manual/en/function.ob-get-level.php - E a maneira mais segura de forçar o "teste" forçado é lançar uma exceção BTW.
hakre
3
@DerrickTucker O PHPUnit faz isso chamando phpunit /path/to/tests/theTest.php(se a classe acima estiver no arquivo theTest.php).
Jess Telford
@hakre ob_get_level()retorna 1. No entanto, isso é contradito pelo seguinte código: com while (ob_get_level() > 0) { ob_end_flush(); }que erros ob_end_clean(): failed to delete buffer. No buffer to delete.. Curioso e curioso.
Jess Telford
1
Está dizendo que é o código do phpunit que está causando o erro - obviamente porque a deglutição da saída do phpunits está ativa (mas você o quebrou). Veja com precisão, o nome da função também difere.
hakre

Respostas:

196

ATUALIZAR

Acabei de descobrir outra maneira de fazer isso que funciona muito melhor do que a --verboseopção da linha de comando:

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

Isso permite que você despeje qualquer coisa no seu console a qualquer momento, sem toda a saída indesejada que acompanha a --verboseopção CLI.


Como outras respostas observaram, é melhor testar a saída usando os métodos internos, como:

$this->expectOutputString('foo');

No entanto, às vezes é útil ser impertinente e ver a saída de depuração temporária / única dentro de seus casos de teste. Não há necessidade de var_dumphack / solução alternativa, no entanto. Isso pode ser feito facilmente, definindo a --verboseopção de linha de comando ao executar seu conjunto de testes. Por exemplo:

$ phpunit --verbose -c phpunit.xml

Isso exibirá a saída de dentro de seus métodos de teste ao executar no ambiente da CLI.

Veja: Escrevendo testes para o PHPUnit - Testing Output .

rdlowrey
fonte
5
desculpe, perdemos, escrevemos para stderr. De fato funciona. Eu apenas fui forçado a usar file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);, porque tinha uma mensagem Use of undefined constant STDERR - assumed 'STDERR'com fwrite .
19413 Serge
O problema é que isso não parece funcionar com o isolamento do processo.
precisa saber é o seguinte
@donquixote não é surpreendente como o teste será executado em outro processo cuja STDERR saída de fluxo é provável descartado ...
rdlowrey
1
Você pode aslo uso STDOUTem vez deSTERR
Chris
2
Sim. Funciona e parece produzir da mesma maneira que STDERR. Eu estou usando PHPUnit 4.5.0na linha de cmd do windows. uma echodeclaração não fornece os mesmos resultados. echoproduz, mas somente após o resultado do teste ser exibido. fwrite(STDERR, 'string')ou fwrite(STDOUT,'string')produza os mesmos resultados: Uma saída antes do resultado do teste ser exibido.
28415 Chris
33

Atualização: Veja a atualização de rdlowrey abaixo sobre o uso de fwrite(STDERR, print_r($myDebugVar, TRUE));uma solução muito mais simples


Esse comportamento é intencional (como jasonbar tem apontado ). O estado conflitante do manual foi relatado ao PHPUnit.

Uma solução alternativa é fazer com que o PHPUnit afirme que a saída esperada está vazia (quando na verdade há saída), o que acionará a saída inesperada a ser mostrada.

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.

Certifique-se de desativar quaisquer outras asserções que você possui para o teste, pois elas podem falhar antes que a asserção de saída seja testada (e, portanto, você não verá a saída).

Jess Telford
fonte
33

Tente usar --debug

Útil se você estiver tentando encontrar o caminho certo para um arquivo de dados de inclusão ou origem.

chim
fonte
2
Esta é a resposta correta para mim. Todas as declarações escritas nas respostas anteriores não funcionaram para mim.
Kim Stacks
9

Não é um bug, mas muito intencional. Sua melhor aposta é gravar em um arquivo de log de algum tipo e seguir o log para observar a saída.

Se você estiver tentando testar a saída, verifique isso .

Além disso:

Nota : Observe que o PHPUnit engole toda a saída emitida durante a execução de um teste. No modo estrito, um teste que emite saída falhará.

jasonbar
fonte
1
Se fosse intencional, certamente o manual não daria um exemplo disso ? Além disso, não tentando testar a saída em si. Basta usá-lo para observar alguns resultados, causando falhas nos testes quando não deveriam.
Jess Telford
Como está escrito: Normalmente, não tenho problemas para ecoar quando os testes são executados. Você pode ter alguma configuração que está captando informações.
hakre 21/09/11
1
Se não fosse intencional, certamente o manual não diria que sim .
Jasonbar
1
Portanto, parece um conflito na documentação. @hakre parece estar com a mesma impressão que eu estava (que não deveria ser engolido) - qual parte da documentação está correta?
Jess Telford
Os testes de geração de saída SOMENTE falham quando --disallow-test-output (ou o arquivo conf tem beStrictAboutOutputDuringTests = "true") - a documentação agora diz "Um teste que emite saída, por exemplo, chamando print no código de teste ou no código testado, será marcado como arriscado quando esta verificação estiver ativada ". phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
ponteiro NULL
7

Estou tendo alguma sorte com o VisualPHPUnit , e ele mostra resultados úteis, entre outras coisas.

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

Olá, resultados

Bob Stein
fonte
Hmm, por que o voto negativo? Como isso não é útil como uma maneira alternativa de despejar a saída de depuração em um teste PHPUnit?
Bob Stein
1
Eu estou supondo que este é downvoted porque se alguém tentar executar este você vai ter um erro de sintaxe. Um enorme.
Jimbo
Esqueci a função. Agora está consertado, testado, cortado e colado. Obrigado, @Jimbo
Bob Stein
Infelizmente, não é compatível com o PHP 7 no momento, aparentemente: "O VisualPHPUnit não é compatível com o php 7 no momento devido à maneira como o phpunit é utilizado. O php 7 será suportado na próxima versão principal"
leo
6

Você realmente deve pensar em suas intenções: se precisar das informações agora ao depurar para corrigir o teste, precisará delas na próxima semana novamente quando os testes forem interrompidos.

Isso significa que você precisará das informações sempre que o teste falhar - e adicionar um var_dumppara encontrar a causa é muito trabalhoso. Em vez disso, coloque os dados em suas afirmações.

Se o seu código for muito complexo para isso, divida-o até atingir um nível em que uma asserção (com uma mensagem personalizada) diga o suficiente para saber onde ele quebrou, por que e como corrigi-lo.

cweiske
fonte
1
Eu concordo 100% com tudo o que você disse. Estou usando o PHPUnit para fazer testes de integração que finalmente chamam uma das APIs XML do Google. Todos os testes de unidade foram aprovados (com chamadas de API simuladas), mas o teste final (com chamadas de API ativas) falhou. Acontece que a culpa foi da API do Google, mas, enquanto isso, eu queria despejar a resposta HTTP bruta.
Jess Telford
2
E se você precisar depurar seu código no caminho para alcançar o que você descreveu aqui?
David Meister
2
É por isso que não gosto de respostas que adivinhem o que os usuários querem fazer. Estou aqui porque tenho um teste que aguarda a limpeza do cache. Com 5 segundos de cache ttls, isso significa que meu teste parece travar por ~ 16 segundos. Gostaria apenas de emitir algum aviso ao usuário de que não, não há nada errado, estamos apenas aguardando o tempo limite dos caches. Se as pessoas puderem responder apenas à pergunta, as pessoas com outros casos de uso também terão suas respostas.
user151841
4

No laravel 5, você pode usar dump (), despejar o conteúdo da última resposta.

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

Branny Bk
fonte
4

Basta usar a flag --verbose ao executar o phpunit .

$ phpunit --verbose -c phpunit.xml 

A vantagem desse método é que você não precisa alterar o código de teste, pode imprimir strings, var_dump ou o que quiser sempre, e ele será mostrado no console apenas quando o modo detalhado estiver definido.

Eu espero que isso ajude.

Fabricio
fonte
3

Em alguns casos, pode-se usar algo parecido para gerar algo para o 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
fonte
3

Hackish, mas funciona: Lance uma exceção com a saída de depuração como sua mensagem.

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

Rendimentos:

...
There was 1 error:

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

Isso foi retirado do PHPUnit Docs sobre acessórios .

Isso deve permitir que você despeje informações a qualquer momento durante o ciclo de vida do teste phpunit.

Basta substituir __METHOD__no código abaixo o que você deseja exibir

Exemplo 4.2: Exemplo mostrando todos os métodos de modelo disponíveis

<?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
fonte
1

Eu produzi meus resultados de teste baseados em HTML; nesse caso, foi útil liberar o conteúdo:

var_dump($array);
ob_flush();

Existe um segundo método PHP

flush() 

que eu não tentei.

Sudo
fonte
1

O PHPUnit está ocultando a saída com ob_start(). Podemos desativá-lo temporariamente.

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

Eu tive que modificar o código fonte para que esse código funcionasse, então você precisa adicionar o URL desses repositórios bifurcados ao compositor, pois isso funcionará

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

Aqui estão alguns métodos úteis para imprimir mensagens de depuração no PHPUnit 4.x:

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

    Exemplo mais prático:

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

    A chamada syslog()gerará uma mensagem de log do sistema (consulte man syslog.conf:).

    Nota: Níveis possíveis: LOG_DEBUG , LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, etc.

    No macOS, para transmitir as mensagens syslog em tempo real, execute:

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

    Nota: A STDERRconstante não estará disponível se estiver lendo o script PHP em stdin . Aqui está a solução alternativa .

    Nota: em vez de STDERR , você também pode especificar um nome de arquivo.

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

    Nota: Use este método, se você não tiver STDERRconstante definida.

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

    Nota: Use esse método se desejar imprimir algo no final, sem afetar os testes.

Para despejar a variável, use var_export(), por exemplo "Value: " . var_export($some_var, TRUE) . "\n".

Para imprimir as mensagens acima apenas durante o modo detalhado ou de depuração, consulte: Existe uma maneira de saber se --debug ou --verbose foi passado para o PHPUnit em um teste?


Embora se testar a saída faça parte do teste, verifique: Página de documentação Testando saída .

kenorb
fonte
-1

Se você usa o Laravel, pode usar funções de registro como info () para registrar no arquivo de log do Laravel em armazenamento / registros. Portanto, ele não aparecerá no seu terminal, mas no arquivo de log.

leo
fonte