A esas respuestas: ¿qué pasa con las aserciones múltiples en una función de prueba, y solo espero tener una excepción de lanzamiento? ¿Tengo que separarlos y poner el uno en una función de prueba independiente?
Panwen Wang
Respuestas:
550
<?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);}}
Si usa espacios de nombres, debe ingresar el espacio de nombres completo:$this->setExpectedException('\My\Name\Space\MyCustomException');
Alcalyn
15
El hecho de que no pueda designar la línea precisa de código que se espera que arroje, es un error de la OMI. Y la imposibilidad de probar más de una excepción en la misma prueba hace que probar muchas excepciones esperadas sea un asunto realmente complicado. Escribí una afirmación real para tratar de resolver esos problemas.
mindplay.dk
18
FYI: a partir del método phpunit 5.2.0setExpectedException está en desuso, reemplazado por el expectExceptionuno. :)
hejdav
41
Lo que no se menciona en los documentos o aquí, pero el código que se espera arroje una excepción debe llamarse despuésexpectException() . Si bien podría haber sido obvio para algunos, fue una de gotcha para mí.
Jason McCreary
77
No es obvio del documento, pero no se ejecutará ningún código después de su función que arroje una excepción. Entonces, si desea probar varias excepciones en el mismo caso de prueba, no puede hacerlo.
@LeviMorrison: en mi humilde opinión, el mensaje de excepción no debe probarse, de manera similar a los mensajes de registro. Ambos se consideran información extraña y útil cuando se realizan análisis forenses manuales . El punto clave a probar es el tipo de excepción. Cualquier cosa más allá de eso se vincula demasiado a la implementación. IncorrectPasswordExceptiondebería ser suficiente: que el mensaje sea igual "Wrong password for [email protected]"es auxiliar. Agregue a eso que desea pasar el menor tiempo posible escribiendo pruebas, y comenzará a ver cuán importantes se vuelven las pruebas simples.
David Harkness
55
@DavidHarkness, pensé que alguien mencionaría eso. Del mismo modo, estaría de acuerdo en que probar los mensajes en general es demasiado estricto y estricto. Sin embargo, es lo estricto y lo estricto que puede (enfatizado a propósito) lo que se desea en algunas situaciones, como la aplicación de una especificación.
Levi Morrison
1
No miraría en un doc-block para entender lo que esperaba, pero miraría el código de prueba real (independientemente del tipo de prueba). Ese es el estándar para todas las otras pruebas; No veo razones válidas para que las Excepciones sean (oh, Dios) una excepción a esta convención.
Kamafeather
3
La regla "no probar el mensaje" suena válida, a menos que pruebe un método que arroje el mismo tipo de excepción en varias partes del código, con la única diferencia que es la identificación del error, que se pasa en el mensaje. Su sistema puede mostrar un mensaje al usuario en función del mensaje de excepción (no del tipo de excepción). En ese caso, no importa qué mensaje vea el usuario, por lo tanto, debe probar el mensaje de error.
El siguiente código probará el mensaje de excepción y el código de excepción.
Importante: fallará si la excepción esperada no se produce también.
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()no está destinado a ser utilizado de esta manera, no creo, al menos no actualmente (PHPUnit 3.6.11); Actúa como una excepción en sí misma. Usando su ejemplo, si $this->fail("Expected exception not thrown")se llama, entonces el catchbloque se dispara y $e->getMessage()es "Excepción esperada no lanzada" .
Ken
1
@ken probablemente tengas razón. La llamada a failprobablemente pertenece después del bloque catch, no dentro del intento.
Frank Farmer
1
Tengo que votar a favor porque la llamada a failno debe estar en el trybloque. En sí mismo, activa el catchbloqueo produciendo resultados falsos.
Twifty
66
Creo que la razón por la que esto no funciona bien es que, en alguna situación, está atrapando todas las excepciones catch(Exception $e). Este método funciona bastante bien para mí cuando trato de capturar excepciones específicas:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
spyle
23
Puede usar la extensión ClaimException para afirmar más de una excepción durante la ejecución de una prueba.
Inserte el método en su TestCase y use:
publicfunction testSomething(){
$test =function(){// some code that has to throw an exception};
$this->assertException( $test,'InvalidArgumentException',100,'expected message');}
También hice un rasgo para los amantes del buen código ...
¿Qué PHPUnit estás usando? Estoy usando PHPUnit 4.7.5, y no assertExceptionestá definido. Tampoco puedo encontrarlo en el manual de PHPUnit.
physicalattraction
2
El asertExceptionmétodo no es parte de PHPUnit original. Debe heredar la PHPUnit_Framework_TestCaseclase y agregar el método vinculado en la publicación anterior manualmente. Sus casos de prueba heredarán esta clase heredada.
Sin duda, la mayor cantidad de azúcar en esta sintaxis
AndrewMcLagan
13
El expectExceptionmétodo PHPUnit es muy inconveniente porque permite probar solo una excepción por método de prueba.
He hecho esta función auxiliar para afirmar que alguna función arroja una excepción:
/**
* 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');}
Agréguelo a su clase de prueba y llame de esta manera:
Solo para ilustrar el espíritu detrás de la sintaxis:
<?php
// Using simple callback
$this->assertThrows(MyException::class,[$obj,'doSomethingBad']);// Using anonymous function
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();});
¿Con buena pinta?
Ejemplo de uso completo
Consulte a continuación un ejemplo de uso más completo:
<?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();});}}?>
Es un poco irónico que su paquete para pruebas unitarias no incluya pruebas unitarias en el repositorio.
domdambrogia
2
@domdambrogia gracias a @ jean-beguin ahora tiene pruebas unitarias.
jchook
8
publicfunction testException(){try{
$this->methodThatThrowsException();
$this->fail("Expected Exception has not been raised.");}catch(Exception $ex){
$this->assertEquals($ex->getMessage(),"Exception message");}}
La firma de assertEquals()es assertEquals(mixed $expected, mixed $actual...)inversa, como en su ejemplo, por lo que debería ser$this->assertEquals("Exception message", $ex->getMessage());
Roger Campanera
7
Aquí están todas las afirmaciones de excepción que puede hacer. Tenga en cuenta que todos ellos son opcionales .
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!');}}
Es incorrecto porque PHP se detiene en la primera excepción lanzada. PHPUnit comprueba que la excepción lanzada tiene el tipo correcto y dice «la prueba está bien», ni siquiera sabe acerca de la segunda excepción.
Finesse
3
/**
* @expectedException Exception
* @expectedExceptionMessage Amount has to be bigger then 0!
*/publicfunction testDepositNegative(){
$this->account->deposit(-7);}
Tenga mucho cuidado "/**", observe el doble "*". Escribir solo "**" (asterisco) fallará su código. También asegúrese de estar usando la última versión de phpUnit. En algunas versiones anteriores de phpunit @expectedException Exception no es compatible. Tenía 4.0 y no me funcionó, tuve que actualizar a 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer para actualizar con el compositor.
Para PHPUnit 5.7.27 y PHP 5.6 y para probar varias excepciones en una prueba, era importante forzar la prueba de excepción. El uso de manejo de excepciones solo para afirmar la instancia de Excepción omitirá probar la situación si no ocurre una excepción.
function yourfunction($a,$z){if($a<$z){thrownew<YOUR_EXCEPTION>;}}
aqui esta la prueba
classFunctionTestextends \PHPUnit_Framework_TestCase{publicfunction testException(){
$this->setExpectedException(<YOUR_EXCEPTION>::class);
yourfunction(1,2);//add vars that cause the exception }}
PhpUnit es una biblioteca increíble, pero este punto específico es un poco frustrante. Es por eso que podemos usar la biblioteca de código abierto turbotesting-php que tiene un método de afirmación muy conveniente para ayudarnos a probar excepciones. Se encuentra aquí:
AssertUtils::throwsException(function(){// Some code that must throw an exception here},'/expected error message/');
Si el código que escribimos dentro de la función anónima no produce una excepción, se generará una excepción.
Si el código que escribimos dentro de la función anónima produce una excepción, pero su mensaje no coincide con la expresión regular esperada, también se generará una excepción.
Respuestas:
expectException () documentación de PHPUnit
El artículo del autor PHPUnit proporciona una explicación detallada sobre las mejores prácticas de excepciones de prueba.
fuente
$this->setExpectedException('\My\Name\Space\MyCustomException');
setExpectedException
está en desuso, reemplazado por elexpectException
uno. :)expectException()
. Si bien podría haber sido obvio para algunos, fue una de gotcha para mí.También puede usar una anotación docblock hasta que se lance PHPUnit 9:
Para PHP 5.5+ (especialmente con código de espacio de nombres), ahora prefiero usar
::class
fuente
IncorrectPasswordException
debería ser suficiente: que el mensaje sea igual"Wrong password for [email protected]"
es auxiliar. Agregue a eso que desea pasar el menor tiempo posible escribiendo pruebas, y comenzará a ver cuán importantes se vuelven las pruebas simples.Si está ejecutando PHP 5.5+, puede usar la
::class
resolución para obtener el nombre de la clase conexpectException
/setExpectedException
. Esto proporciona varios beneficios:string
modo que funcionará con cualquier versión de PHPUnit.Ejemplo:
PHP compila
dentro
sin PHPUnit siendo el más sabio.
fuente
El siguiente código probará el mensaje de excepción y el código de excepción.
Importante: fallará si la excepción esperada no se produce también.
fuente
$this->fail()
no está destinado a ser utilizado de esta manera, no creo, al menos no actualmente (PHPUnit 3.6.11); Actúa como una excepción en sí misma. Usando su ejemplo, si$this->fail("Expected exception not thrown")
se llama, entonces elcatch
bloque se dispara y$e->getMessage()
es "Excepción esperada no lanzada" .fail
probablemente pertenece después del bloque catch, no dentro del intento.fail
no debe estar en eltry
bloque. En sí mismo, activa elcatch
bloqueo produciendo resultados falsos.catch(Exception $e)
. Este método funciona bastante bien para mí cuando trato de capturar excepciones específicas:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
Puede usar la extensión ClaimException para afirmar más de una excepción durante la ejecución de una prueba.
Inserte el método en su TestCase y use:
También hice un rasgo para los amantes del buen código ...
fuente
assertException
está definido. Tampoco puedo encontrarlo en el manual de PHPUnit.asertException
método no es parte de PHPUnit original. Debe heredar laPHPUnit_Framework_TestCase
clase y agregar el método vinculado en la publicación anterior manualmente. Sus casos de prueba heredarán esta clase heredada.Una forma alternativa puede ser la siguiente:
Asegúrese de que su clase de prueba se extienda
\PHPUnit_Framework_TestCase
.fuente
El
expectException
método PHPUnit es muy inconveniente porque permite probar solo una excepción por método de prueba.He hecho esta función auxiliar para afirmar que alguna función arroja una excepción:
Agréguelo a su clase de prueba y llame de esta manera:
fuente
Solución integral
Las " mejores prácticas " actuales de PHPUnit para pruebas de excepción parecen ... mediocres ( docs ).
Como quería más que la
expectException
implementación actual , hice un rasgo para usar en mis casos de prueba. Son solo ~ 50 líneas de código .assert
Sintaxis estándarassertNotThrows
Throwable
errores PHP 7Biblioteca
Publiqué el
AssertThrows
rasgo en Github y Packagist para que pueda instalarse con el compositor.Ejemplo simple
Solo para ilustrar el espíritu detrás de la sintaxis:
¿Con buena pinta?
Ejemplo de uso completo
Consulte a continuación un ejemplo de uso más completo:
fuente
fuente
assertEquals()
esassertEquals(mixed $expected, mixed $actual...)
inversa, como en su ejemplo, por lo que debería ser$this->assertEquals("Exception message", $ex->getMessage());
Aquí están todas las afirmaciones de excepción que puede hacer. Tenga en cuenta que todos ellos son opcionales .
La documentación se puede encontrar aquí .
fuente
Tenga mucho cuidado
"/**"
, observe el doble "*". Escribir solo "**" (asterisco) fallará su código. También asegúrese de estar usando la última versión de phpUnit. En algunas versiones anteriores de phpunit @expectedException Exception no es compatible. Tenía 4.0 y no me funcionó, tuve que actualizar a 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer para actualizar con el compositor.fuente
Para PHPUnit 5.7.27 y PHP 5.6 y para probar varias excepciones en una prueba, era importante forzar la prueba de excepción. El uso de manejo de excepciones solo para afirmar la instancia de Excepción omitirá probar la situación si no ocurre una excepción.
fuente
aqui esta la prueba
fuente
PhpUnit es una biblioteca increíble, pero este punto específico es un poco frustrante. Es por eso que podemos usar la biblioteca de código abierto turbotesting-php que tiene un método de afirmación muy conveniente para ayudarnos a probar excepciones. Se encuentra aquí:
https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php
Y para usarlo, simplemente haríamos lo siguiente:
Si el código que escribimos dentro de la función anónima no produce una excepción, se generará una excepción.
Si el código que escribimos dentro de la función anónima produce una excepción, pero su mensaje no coincide con la expresión regular esperada, también se generará una excepción.
fuente