Pergunta

Esta questão é específico para usando PHPUnit.

PHPUnit converte automaticamente erros php para exceções. Existe uma maneira de testar o valor de retorno de um método que acontece para acionar um erro de php (ou built-in erros ou usuário erros gerados via trigger_error )?

Exemplo de código para teste:

function load_file ($file)
{
    if (! file_exists($file)) {
        trigger_error("file {$file} does not exist", E_USER_WARNING);
        return false;
    }
    return file_get_contents($file);
}

Este é o tipo de teste que eu quero escrever:

public function testLoadFile ()
{
    $this->assertFalse(load_file('/some/non-existent/file'));
}

O problema que estou tendo é que o erro desencadeada faz com que meu teste de unidade falhe (como deveria). Mas se eu tentar pegá-lo, ou definir uma exceção esperada qualquer código que após o erro é acionado nunca executa então eu não tenho nenhuma maneira de testar o valor de retorno do método.

Este exemplo não funciona:

public function testLoadFile ()
{
    $this->setExpectedException('Exception');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}

Alguma idéia de como eu poderia conseguir isso?

Foi útil?

Solução

Não há nenhuma maneira de fazer isso dentro de um teste de unidade. É possível se você quebrar-se testar o valor de retorno, eo aviso em dois testes diferentes.

manipulador de erro do PHPUnit pega erros PHP e avisos e os converte em Exceções - que por definição pára a execução do programa. A função que você está testando nunca retorna em tudo. Você pode, no entanto, desativar temporariamente a conversão de erros em exceções, mesmo em tempo de execução.

Este é provavelmente mais fácil com um exemplo, então, aqui está o que os dois testes deve ser parecido:

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    $result = load_file('/some/non-existent/file');

}

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);
}

Isto também tem a vantagem adicional de fazer seus testes mais clara, mais limpa e auto documentar.

Re: Comentário: Essa é uma ótima pergunta, e eu não tinha idéia até que eu corri um par de testes. É como se ele irá não restaurar o valor padrão / original, pelo menos a partir de PHPUnit 3.3.17 (a versão estável atual agora).

Então, eu seria realmente alterar a acima para olhar como assim:

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    $warningEnabledOrig = PHPUnit_Framework_Error_Warning::$enabled;
    PHPUnit_Framework_Error_Warning::$enabled = false;

    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);

    PHPUnit_Framework_Error_Warning::$enabled = $warningEnabledOrig;
}

Re: Segundo comentário:

Isso não é totalmente verdade. Eu estou olhando para manipulador de erro do PHPUnit, e funciona da seguinte forma:

  • Se for um E_WARNING, PHPUnit_Framework_Error_Warning uso como uma classe de exceção.
  • Se for um E_NOTICE ou erro E_STRICT, uso PHPUnit_Framework_Error_Notice
  • Else, o uso PHPUnit_Framework_Error como a classe de exceção.

Então, sim, erros da E_USER_* não são transformados em classe * _Warning ou * _Notice do PHPUnit, eles ainda são transformados em uma exceção PHPUnit_Framework_Error genérico.

Outras Pensamentos

Enquanto isso depende exatamente da forma como a função é usada, eu provavelmente mudar para lançar uma exceção real em vez de provocar um erro, se fosse comigo. Sim, isso mudaria o fluxo lógico do método, eo código que usa o método ... agora a execução não parar quando ele não consegue ler um ficheiro. Mas isso é até você para decidir se o arquivo solicitado não existente é verdadeiramente excepcional comportamento. I tendem a usar exceções muito mais do que erros / avisos / notificações, porque eles são mais fáceis de manusear, testar e trabalho em seu fluxo da aplicação. Eu costumo reservar os avisos para coisas como chamadas de método depreciados, etc.

Outras dicas

Use um arquivo de configuração phpunit.xml e desativar o aviso / aviso / erro para conversão de exceção. Mais detalhes no manual. É basicamente algo como isto:

<phpunit convertErrorsToExceptions="false"
         convertNoticesToExceptions="false"
         convertWarningsToExceptions="false">
</phpunit>

Em vez de esperar uma "Exception" genérico, o que acontece com esperando um "PHPUnit_Framework_Error"?

Algo como isso pode fazer:

/**
 * @expectedException PHPUnit_Framework_Error
 */
public function testFailingInclude()
{
    include 'not_existing_file.php';
}

O que, suponho, também pode ser escrita como:

public function testLoadFile ()
{
    $this->setExpectedException('PHPUnit_Framework_Error');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}

Para mais informações, consulte Erros Testing PHP
Especialmente, ele diz (citando):

PHPUnit_Framework_Error_Notice e PHPUnit_Framework_Error_Warning representam PHP percebe e alerta, respectivamente.


Olhando para o arquivo /usr/share/php/PHPUnit/TextUI/TestRunner.php eu tenho no meu sistema, eu vejo esta (linha 198 e seguintes) :

if (!$arguments['convertNoticesToExceptions']) {
    PHPUnit_Framework_Error_Notice::$enabled = FALSE;
}

if (!$arguments['convertWarningsToExceptions']) {
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
}

Então, talvez você vai ter que passar algum tipo de parâmetro para ativar esse comportamento? Mas parece ser ativado por padrão ...

Na verdade, há uma maneira de testar tanto o valor de retorno e a exceção lançada (neste caso, um erro convertido por PHPUnit).

Você apenas tem que fazer o seguinte:

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->assertFalse(@load_file('/some/non-existent/file'));

    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    load_file('/some/non-existent/file');
}

Observe que para testar o valor de retorno que você tem que usar o operador de supressão de erro na chamada de função (o @ antes o nome da função). Desta forma, nenhuma exceção será lançada e a execução continuará. Você então tem que definir a exceção esperada, como de costume para testar o erro.

O que você não pode fazer é testar várias exceções dentro de uma unidade de teste.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top