2009-08-04 11 views
11

Esta pregunta es específica de PHPUnit.pruebe el valor de retorno de un método que desencadena un error con PHPUnit

PHPUnit convierte automáticamente los errores de PHP en excepciones. ¿Hay alguna manera de probar el valor de retorno de un método que desencadena un error de php (ya sean errores incorporados o errores generados por el usuario a través del trigger_error)?

Ejemplo de código para la prueba:

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 es el tipo de prueba que quiero escribir:

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

El problema que estoy teniendo es que el error disparada hace que mi unidad de prueba fallar (como debería) Pero si trato de atraparlo o establecer una excepción esperada, cualquier código que después de que se desencadena el error nunca se ejecuta, así que no tengo forma de probar el valor de retorno del método.

Este ejemplo no funciona:

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

    // code after this point never gets executed 

    $this->assertFalse($result); 
} 

¿Alguna idea de cómo podría lograr esto?

Respuesta

21

No hay forma de hacerlo en una prueba unitaria. Es posible si rompe la prueba del valor de retorno y el aviso en dos pruebas diferentes.

El controlador de errores de PHPUnit detecta errores y avisos de PHP y los convierte en Excepciones, lo que por definición detiene la ejecución del programa. La función que está probando nunca regresa. Sin embargo, puede desactivar temporalmente la conversión de errores en excepciones, incluso en tiempo de ejecución.

Esto es probablemente más fácil con un ejemplo, es así, esto es lo que las dos pruebas debe ser similar:

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); 
} 

Esto también tiene la ventaja añadida de hacer sus pruebas más claras y más limpio autodocumentado,.

Re: Comentario: Esa es una gran pregunta, y no tenía ni idea hasta que me encontré con un par de pruebas. Parece que va a no restaurar el valor predeterminado/original, al menos a partir de PHPUnit 3.3.17 (la versión estable actual en este momento).

Por lo tanto, no me habría importado modificar lo anterior para buscar así:

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 comentario:

Eso no es del todo cierto. Estoy mirando gestor de errores de PHPUnit, y funciona de la siguiente manera:

  • Si se trata de un E_WARNING, utilice PHPUnit_Framework_Error_Warning como una clase de excepción.
  • Si se trata de un error o E_NOTICEE_STRICT, utilice PHPUnit_Framework_Error_Notice
  • Si no, utilizar PHPUnit_Framework_Error como la clase de excepción.

Así que, sí, los errores de la E_USER_* no son convertidos en PHPUnit de _ADVERTENOIA * o * Clase _Notice, todavía se transforman en un genérico PHPUnit_Framework_Error excepción.

Más ideas

Si bien depende exactamente de cómo se utiliza la función, probablemente cambien a lanzar una excepción real en lugar de desencadenar un error, si fuera yo. Sí, esto cambiaría el flujo lógico del método y el código que usa el método ... en este momento la ejecución no se detiene cuando no puede leer un archivo. Pero eso depende de usted para decidir si el archivo solicitado que no existe es verdaderamente comportamiento excepcional. Tiendo a usar excepciones mucho más que errores/advertencias/avisos, porque son más fáciles de manejar, probar y trabajar en el flujo de aplicaciones. Normalmente reservo avisos para cosas como llamadas a métodos depreciados, etc.

+0

Gracias jason. ¿Sabe por casualidad si el valor PHPUnit_Framework_Error_Warning :: $ enabled se revierte automáticamente entre las pruebas? ¿O necesita cambiarlo manualmente a su valor original? – dellsala

+0

Una nota sobre esta respuesta. Aunque funciona para los errores generados por las funciones y los métodos de php incorporados, no parece funcionar para los tipos de error generados por el usuario (E_USER_WARNING y E_USER_NOTICE) trigger_error. Parece que PHPUnit no admite alternar estos en el tiempo de ejecución (versión 3.3.17) – dellsala

3

En lugar de esperar un "Exception" genérico, ¿qué hay de esperar un "PHPUnit_Framework_Error"?

Algo como esto podría hacer:

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

Lo cual, supongo, también puede ser escrito 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 más información, véase Testing PHP Errors
Especialmente, se dice (citando) :

PHPUnit_Framework_Error_Notice y PHPUnit_Framework_Error_Warning representa avisos de PHP y advertencia, respectivamente.


Mirando el archivo /usr/share/php/PHPUnit/TextUI/TestRunner.php tengo en mi sistema, veo esto (línea 198 y siguientes):

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

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

¿Entonces tal vez tendrá que pasar algún tipo de parámetro para activar ese comportamiento? Pero parece estar habilitado por defecto ...

+0

Esto no es lo que estaba preguntando. – jason

+0

jason tiene razón, no es exactamente lo que estaba preguntando, pero es un buen consejo sobre cómo verificar el tipo específico de error php utilizando los tipos de excepción PHPUnit. – dellsala

+0

@jason: oh, a la derecha :-(lo siento :-(Buena respuesta, por cierto (la cosa sobre el uso de dos pruebas por separado es definitivamente un buen punto) @dellsala: gracias :-) –

8

Utilice un archivo de configuración phpunit.xml y desactive el aviso/advertencia/error en la conversión de excepciones. Más details in the manual. Básicamente es algo como esto:

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

@lonut - Estaba buscando _this_ ! Publicación de 2 años, aún relevante. – stefgosselin

1

En realidad, hay una manera de probar tanto el valor de retorno como la excepción lanzada (en este caso un error convertido por PHPUnit).

Sólo tienes que hacer lo siguiente:

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'); 
} 

Tenga en cuenta que para poner a prueba el valor de retorno que tiene que utilizar el operador de supresión de error en la llamada a la función (el @ antes de que el nombre de la función). De esta forma, no se lanzará ninguna excepción y la ejecución continuará.Luego debe establecer la excepción esperada como de costumbre para probar el error.

Lo que no puede hacer es probar múltiples excepciones dentro de una prueba unitaria.

Cuestiones relacionadas