Para essas respostas: e as multi-asserções em uma função de teste, e só espero ter uma exceção de lançamento? Tenho que separá-los e colocá-lo em uma função de teste independente?
Panwen Wang
Respostas:
549
<?php
require_once 'PHPUnit/Framework.php';classExceptionTestextendsPHPUnit_Framework_TestCase{publicfunction testException(){
$this->expectException(InvalidArgumentException::class);// or for PHPUnit < 5.2// $this->setExpectedException(InvalidArgumentException::class);//...and then add your test code that generates the exception
exampleMethod($anInvalidArgument);}}
Se você usa espaços para nome, é necessário inserir o espaço para nome completo:$this->setExpectedException('\My\Name\Space\MyCustomException');
Alcalyn 25/11
15
O fato de você não poder designar a linha de código precisa que deve ser lançada é um erro IMO. E a incapacidade de testar mais de uma exceção no mesmo teste, torna o teste de muitas exceções esperadas um assunto realmente desajeitado. Eu escrevi uma afirmação real para tentar resolver esses problemas.
mindplay.dk
18
Para sua informação: a partir do phpunit 5.2.0, osetExpectedException método foi descontinuado e substituído pelo expectException. :)
hejdav
41
O que não é mencionado nos documentos ou aqui, mas o código esperado para lançar uma exceção precisa ser chamado depoisexpectException() . Embora possa ter sido óbvio para alguns, era uma pegadinha para mim.
21417 Jason Mclreary
7
Não é óbvio no documento, mas nenhum código após a sua função que lança uma exceção será executado. Portanto, se você quiser testar várias exceções no mesmo caso de teste, não poderá.
30517 laurent
122
Você também pode usar uma anotação de docblock até que o PHPUnit 9 seja lançado:
@LeviMorrison - IMHO a mensagem de exceção não deve ser testada, da mesma forma que as mensagens de log. Ambos são considerados informações úteis e estranhas ao executar análise forense manual . O ponto principal a ser testado é o tipo de exceção. Qualquer coisa além disso está vinculando muito fortemente à implementação. IncorrectPasswordExceptiondeve ser suficiente - para que a mensagem "Wrong password for [email protected]"seja igual a auxiliar. Acrescente a isso que você deseja gastar o menor tempo possível escrevendo testes e comece a ver a importância dos testes simples.
precisa saber é o seguinte
5
@DavidHarkness Imaginei que alguém traria isso à tona. Da mesma forma, eu concordaria que o teste de mensagens em geral é muito rigoroso e rígido. No entanto, é esse rigor e o vínculo estreito que podem (enfatizado propositalmente) ser o que se deseja em algumas situações, como a aplicação de uma especificação.
Levi Morrison
11
Eu não assistiria em um bloco de documentos para entender o que esperava, mas veria o código de teste real (independentemente do tipo de teste). Esse é o padrão para todos os outros testes; Não vejo razões válidas para exceções serem (oh deus) uma exceção a esta convenção.
Kamafeather
3
A regra "não testar a mensagem" parece válida, a menos que você teste um método que lança o mesmo tipo de exceção em várias partes do código, com a única diferença sendo o ID do erro, que é transmitido na mensagem. Seu sistema pode exibir uma mensagem para o usuário com base na mensagem de exceção (não no tipo de exceção). Nesse caso, importa qual mensagem o usuário vê; portanto, você deve testar a mensagem de erro.
O código abaixo testará a mensagem de exceção e o código de exceção.
Importante: Ele falhará se a exceção esperada também não for lançada.
try{
$test->methodWhichWillThrowException();//if this method not throw exception it must be fail too.
$this->fail("Expected exception 1162011 not thrown");}catch(MySpecificException $e){//Not catching a generic Exception or the fail function is also catched
$this->assertEquals(1162011, $e->getCode());
$this->assertEquals("Exception Message", $e->getMessage());}
$this->fail()não é para ser usado dessa maneira, não acho, pelo menos não atualmente (PHPUnit 3.6.11); atua como uma exceção em si. Usando seu exemplo, se $this->fail("Expected exception not thrown")for chamado, o catchbloco será acionado e $e->getMessage()será "Exceção esperada não lançada" .
ken
11
@ken você provavelmente está certo. A chamada para failprovavelmente pertence após o bloco catch, não dentro da tentativa.
Frank fazendeiro
11
Eu tenho que reduzir o voto porque a chamada para failnão deve estar no trybloco. Ele, por si só, aciona o catchbloco, produzindo resultados falsos.
Twifty
6
Acredito que a razão pela qual isso não funcione bem é uma situação em que está capturando todas as exceções catch(Exception $e). Este método funciona muito bem para mim quando eu tento capturar exceções específicas:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
spyle
23
Você pode usar a extensão assertException para afirmar mais de uma exceção durante a execução de um teste.
Insira o método no seu TestCase e use:
publicfunction testSomething(){
$test =function(){// some code that has to throw an exception};
$this->assertException( $test,'InvalidArgumentException',100,'expected message');}
Eu também fiz uma característica para os amantes do bom código ..
Qual PHPUnit você está usando? Estou usando o PHPUnit 4.7.5 e não assertExceptionestá definido. Também não consigo encontrá-lo no manual do PHPUnit.
physicalattraction
2
O asertExceptionmétodo não faz parte do PHPUnit original. Você deve herdar a PHPUnit_Framework_TestCaseclasse e adicionar o método vinculado na postagem acima manualmente. Seus casos de teste herdarão essa classe herdada.
O expectExceptionmétodo PHPUnit é muito inconveniente, pois permite testar apenas uma exceção por método de teste.
Eu fiz essa função auxiliar para afirmar que alguma função lança uma exceção:
/**
* Asserts that the given callback throws the given exception.
*
* @param string $expectClass The name of the expected exception class
* @param callable $callback A callback which should throw the exception
*/protectedfunction assertException(string $expectClass, callable $callback){try{
$callback();}catch(\Throwable $exception){
$this->assertInstanceOf($expectClass, $exception,'An invalid exception was thrown');return;}
$this->fail('No exception was thrown');}
Adicione-o à sua classe de teste e chame desta maneira:
Definitivamente, a melhor solução de todas as respostas! Jogue-o em uma característica e empacote-o!
domdambrogia
11
Solução abrangente
As " melhores práticas " atuais do PHPUnit para testes de exceção parecem .. sem brilho ( docs ).
Como eu queria mais do que a expectExceptionimplementação atual , criei uma característica para usar nos meus casos de teste. São apenas ~ 50 linhas de código .
Suporta várias exceções por teste
Suporta asserções chamadas após a exceção ser lançada
Exemplos de uso claros e robustos
assertSintaxe padrão
Suporta asserções para mais do que apenas mensagem, código e classe
Apenas para ilustrar o espírito por trás da sintaxe:
<?php
// Using simple callback
$this->assertThrows(MyException::class,[$obj,'doSomethingBad']);// Using anonymous function
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();});
Muito arrumado?
Exemplo de uso completo
Veja abaixo um exemplo de uso mais abrangente:
<?php
declare(strict_types=1);useJchook\AssertThrows\AssertThrows;usePHPUnit\Framework\TestCase;// These are just for illustrationuseMyNamespace\MyException;useMyNamespace\MyObject;finalclassMyTestextendsTestCase{useAssertThrows;// <--- adds the assertThrows methodpublicfunction testMyObject(){
$obj =newMyObject();// Test a basic exception is thrown
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();});// Test custom aspects of a custom extension class
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();},function($exception){
$this->assertEquals('Expected value', $exception->getCustomThing());
$this->assertEquals(123, $exception->getCode());});// Test that a specific exception is NOT thrown
$this->assertNotThrows(MyException::class,function()use($obj){
$obj->doSomethingGood();});}}?>
Um pouco irônico que seu pacote para teste de unidade não inclua testes de unidade no repositório.
domdambrogia
2
@domdambrogia graças a @ jean-beguin , agora possui testes de unidade.
jchook
8
publicfunction testException(){try{
$this->methodThatThrowsException();
$this->fail("Expected Exception has not been raised.");}catch(Exception $ex){
$this->assertEquals($ex->getMessage(),"Exception message");}}
A assinatura de assertEquals()é assertEquals(mixed $expected, mixed $actual...), inverter como no seu exemplo, por isso deveria ser$this->assertEquals("Exception message", $ex->getMessage());
Roger Campanera
7
Aqui estão todas as asserções de exceção que você pode fazer. Observe que todos eles são opcionais .
classExceptionTestextendsPHPUnit_Framework_TestCase{publicfunction testException(){// make your exception assertions
$this->expectException(InvalidArgumentException::class);// if you use namespaces:// $this->expectException('\Namespace\MyException');
$this->expectExceptionMessage('message');
$this->expectExceptionMessageRegExp('/essage$/');
$this->expectExceptionCode(123);// code that throws an exceptionthrownewInvalidArgumentException('message',123);}publicfunction testAnotherException(){// repeat as needed
$this->expectException(Exception::class);thrownewException('Oh no!');}}
Está incorreto porque o PHP para na primeira exceção lançada. O PHPUnit verifica se a exceção lançada tem o tipo correto e diz "o teste está bom", nem mesmo sabe sobre a segunda exceção.
quer
3
/**
* @expectedException Exception
* @expectedExceptionMessage Amount has to be bigger then 0!
*/publicfunction testDepositNegative(){
$this->account->deposit(-7);}
Tenha muito cuidado "/**", observe o duplo "*". Escrever apenas "**" (asterix) falhará no seu código. Certifique-se também de usar a última versão do phpUnit. Em algumas versões anteriores do phpunit @expectedException Exception não é suportado. Eu tinha 4.0 e não funcionou para mim, tive que atualizar para 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer para atualizar com o compositor.
Para o PHPUnit 5.7.27 e PHP 5.6 e para testar várias exceções em um teste, era importante forçar o teste de exceção. Usar o tratamento de exceções sozinho para afirmar a instância de Exceção ignorará o teste da situação se nenhuma exceção ocorrer.
function yourfunction($a,$z){if($a<$z){thrownew<YOUR_EXCEPTION>;}}
aqui está o teste
classFunctionTestextends \PHPUnit_Framework_TestCase{publicfunction testException(){
$this->setExpectedException(<YOUR_EXCEPTION>::class);
yourfunction(1,2);//add vars that cause the exception }}
PhpUnit é uma biblioteca incrível, mas esse ponto específico é um pouco frustrante. É por isso que podemos usar a biblioteca opensource turbotesting-php, que possui um método de asserção muito conveniente para nos ajudar a testar exceções. Pode ser encontrada aqui:
AssertUtils::throwsException(function(){// Some code that must throw an exception here},'/expected error message/');
Se o código digitado dentro da função anônima não gerar uma exceção, uma exceção será lançada.
Se o código que digitarmos dentro da função anônima gerar uma exceção, mas sua mensagem não corresponder ao regexp esperado, uma exceção também será lançada.
Respostas:
documentação do expectException () PHPUnit
O artigo do autor do PHPUnit fornece explicação detalhada sobre o teste das melhores práticas de exceções.
fonte
$this->setExpectedException('\My\Name\Space\MyCustomException');
setExpectedException
método foi descontinuado e substituído peloexpectException
. :)expectException()
. Embora possa ter sido óbvio para alguns, era uma pegadinha para mim.Você também pode usar uma anotação de docblock até que o PHPUnit 9 seja lançado:
Para o PHP 5.5+ (especialmente com código no namespace), agora eu prefiro usar
::class
fonte
IncorrectPasswordException
deve ser suficiente - para que a mensagem"Wrong password for [email protected]"
seja igual a auxiliar. Acrescente a isso que você deseja gastar o menor tempo possível escrevendo testes e comece a ver a importância dos testes simples.Se você estiver executando o PHP 5.5+, poderá usar a
::class
resolução para obter o nome da classe comexpectException
/setExpectedException
. Isso fornece vários benefícios:string
para que ele funcione com qualquer versão do PHPUnit.Exemplo:
Compilações PHP
para dentro
sem PHPUnit ser o mais sábio.
fonte
O código abaixo testará a mensagem de exceção e o código de exceção.
Importante: Ele falhará se a exceção esperada também não for lançada.
fonte
$this->fail()
não é para ser usado dessa maneira, não acho, pelo menos não atualmente (PHPUnit 3.6.11); atua como uma exceção em si. Usando seu exemplo, se$this->fail("Expected exception not thrown")
for chamado, ocatch
bloco será acionado e$e->getMessage()
será "Exceção esperada não lançada" .fail
provavelmente pertence após o bloco catch, não dentro da tentativa.fail
não deve estar notry
bloco. Ele, por si só, aciona ocatch
bloco, produzindo resultados falsos.catch(Exception $e)
. Este método funciona muito bem para mim quando eu tento capturar exceções específicas:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
Você pode usar a extensão assertException para afirmar mais de uma exceção durante a execução de um teste.
Insira o método no seu TestCase e use:
Eu também fiz uma característica para os amantes do bom código ..
fonte
assertException
está definido. Também não consigo encontrá-lo no manual do PHPUnit.asertException
método não faz parte do PHPUnit original. Você deve herdar aPHPUnit_Framework_TestCase
classe e adicionar o método vinculado na postagem acima manualmente. Seus casos de teste herdarão essa classe herdada.Uma maneira alternativa pode ser a seguinte:
Verifique se a sua classe de teste se estende
\PHPUnit_Framework_TestCase
.fonte
O
expectException
método PHPUnit é muito inconveniente, pois permite testar apenas uma exceção por método de teste.Eu fiz essa função auxiliar para afirmar que alguma função lança uma exceção:
Adicione-o à sua classe de teste e chame desta maneira:
fonte
Solução abrangente
As " melhores práticas " atuais do PHPUnit para testes de exceção parecem .. sem brilho ( docs ).
Como eu queria mais do que a
expectException
implementação atual , criei uma característica para usar nos meus casos de teste. São apenas ~ 50 linhas de código .assert
Sintaxe padrãoassertNotThrows
Throwable
erros do PHP 7Biblioteca
Publiquei o
AssertThrows
traço no Github e no packagist para que ele possa ser instalado com o compositor.Exemplo Simples
Apenas para ilustrar o espírito por trás da sintaxe:
Muito arrumado?
Exemplo de uso completo
Veja abaixo um exemplo de uso mais abrangente:
fonte
fonte
assertEquals()
éassertEquals(mixed $expected, mixed $actual...)
, inverter como no seu exemplo, por isso deveria ser$this->assertEquals("Exception message", $ex->getMessage());
Aqui estão todas as asserções de exceção que você pode fazer. Observe que todos eles são opcionais .
A documentação pode ser encontrada aqui .
fonte
Tenha muito cuidado
"/**"
, observe o duplo "*". Escrever apenas "**" (asterix) falhará no seu código. Certifique-se também de usar a última versão do phpUnit. Em algumas versões anteriores do phpunit @expectedException Exception não é suportado. Eu tinha 4.0 e não funcionou para mim, tive que atualizar para 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer para atualizar com o compositor.fonte
Para o PHPUnit 5.7.27 e PHP 5.6 e para testar várias exceções em um teste, era importante forçar o teste de exceção. Usar o tratamento de exceções sozinho para afirmar a instância de Exceção ignorará o teste da situação se nenhuma exceção ocorrer.
fonte
aqui está o teste
fonte
PhpUnit é uma biblioteca incrível, mas esse ponto específico é um pouco frustrante. É por isso que podemos usar a biblioteca opensource turbotesting-php, que possui um método de asserção muito conveniente para nos ajudar a testar exceções. Pode ser encontrada aqui:
https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php
E, para usá-lo, basta fazer o seguinte:
Se o código digitado dentro da função anônima não gerar uma exceção, uma exceção será lançada.
Se o código que digitarmos dentro da função anônima gerar uma exceção, mas sua mensagem não corresponder ao regexp esperado, uma exceção também será lançada.
fonte