2009-08-28 14 views
30

Básicamente tengo un método de una clase llamada killProgram, que está destinado a enviar una redirección hTTP y luego matar a PHP.¿Cómo se usa PHPUnit para probar una función si se supone que esa función matará a PHP?

¿Cómo se supone que voy a probar esto? Cuando ejecuto phpunit, no devuelve nada para esa prueba y se cierra por completo.

En este momento estoy considerando que la función killProgram arroje una excepción que no debería manejarse, lo que me permitiría afirmar que se produjo una excepción.

¿Hay una manera mejor?

Respuesta

22

Como en todos los ensayos se realizan por el mismo proceso PHPUnit, si se utiliza la salida/morir en su código PHP, usted matar todo - como se ha dado cuenta ^^

Por lo tanto, usted tiene que encontrar otra solución, sí, como regresar en lugar de morir; o lanzando una excepción (puede probar si algún código probado arrojó una excepción esperada).

Tal PHPUnit 3.4 y es --process-isolation interruptor (ver Optionally execute each test using a separate PHP process) fuerza ayuda (al no tener todo lo muriendo), pero aún así no sería capaz de obtener el resultado de la prueba, si no lo hace PHPUnit recuperar el control.

He tenido este problema un par de veces; lo resolvió regresando en lugar de morir, incluso regresando varias veces, si es necesario, para volver "lo suficientemente alto" en la pila de llamadas ^^
Al final, supongo que ya no tengo ningún "dado" en mi aplicación ... Es probablemente mejor, cuando se piensa en MVC, por cierto.

+0

Sí, en este momento he decidido lanzar una excepción. El problema es que mi manejador de excepciones predeterminado está destinado a llamar a esta función, así que tengo que crear una nueva excepción llamada killProgramException que mi manejador de excepciones ignora. Tipo de hacky –

7

que se dan cuenta de que ya ha aceptado una respuesta para esto y es una vieja pregunta, pero calculo que esto podría ser útil para alguien, así que aquí va:

En lugar de utilizar die(), podría utilizar throw new RuntimeException() (o una clase de excepción propia), que también detendrá la ejecución del programa (aunque de una manera diferente) y use PHPUnit's setExpectedException() para atraparlo. Si desea que su secuencia de comandos a die() cuando se produce esa excepción, imprimiendo absolutamente nada a nivel del usuario, eche un vistazo a set_exception_handler().

Específicamente, estoy pensando en un escenario en el que coloca el set_exception_handler() -call en un archivo bootstrap que las pruebas no usan, por lo que el controlador no se disparará allí independientemente del escenario, por lo que nada interfiere con el manejo de excepciones nativas de PHPUnit.

+3

Me encanta cuando una publicación anterior responde mi pregunta. – stefgosselin

+0

esta respuesta básicamente sugiere usar excepciones como estructuras de control de flujo, que generalmente no es deseable. –

13

No hay necesidad de cambiar el código solo para poder probarlo, simplemente puede usar set_exit_overload() (provisto por test_helpers del mismo autor que PHPUnit).

+1

Enlace para los perezosos: https://github.com/php-test-helpers/php-test-helpers (Tenga en cuenta que esta es una extensión de PHP que debe activar en su php.ini) –

+1

Nota: Este proyecto es ya no se mantiene (desde 2014). – amphetamachine

3

Esto se relaciona con una serie de problemas que he tenido al obtener un código heredado para pasar una prueba. Así que he llegado con una clase comprobable como esto ...

class Testable { 
    static function exitphp() { 
     if (defined('UNIT_TESTING')) { 
     throw new TestingPhpExitException(); 
     } else { 
     exit(); 
     } 
    } 
} 

Ahora sólo tiene que sustituir las llamadas para salir() con comprobable :: exitphp().

Si está en prueba, solo defino UNIT_TESTING, en producción no lo hago. Parece un simple simulacro.

+0

IMO, esto es muy elegante. Gracias. –

+1

esto podría verse bien al principio, pero esto está cambiando el comportamiento del programa solo por el bien de las pruebas, lo que no sería bueno al final en mi humilde opinión –

16

Obviamente es una vieja pregunta, pero mi sugerencia sería mover el código que die() 's en un método separado que luego puede burlarse.

A modo de ejemplo, en lugar de tener presente:

class SomeClass 
{ 
    public function do() 
    { 
     exit(1); 
     // or 
     die('Message'); 
    } 
} 

hacer esto:

class SomeClass 
{ 
    public function do() 
    { 
     $this->terminate(123); 
     // or 
     $this->terminate('Message'); 
    } 

    protected function terminate($code = 0) 
    { 
     exit($code); 
    } 

    // or 
    protected function terminate($message = '') 
    { 
     die($message); 
    } 
} 

esa manera se puede burlar fácilmente el método terminate y usted no tiene que preocuparse por el script que termina sin que puedas atraparlo.

Su prueba sería algo como esto:

class SomeClassTest extends \PHPUnit_Framework_TestCase 
{ 

    /** 
    * @expectedExceptionCode 123 
    */ 
    public function testDoFail() 
    { 
     $mock = $this->getMock('SomeClass'); 
     $mock->expects($this->any()) 
      ->method('terminate') 
      ->will($this->returnCallback(function($code) { 
       throw new \Exception($code); 
      })); 

     // run to fail 
     $mock->do(); 
    } 
} 

No he probado el código, pero debería ser bastante cercano a un estado de trabajo.

Cuestiones relacionadas