Pregunta

Esta pregunta es específica para usar PHPUnit.

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

Ejemplo de código para probar:

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 tengo es que el error desencadenado hace que mi prueba unitaria falle (como debería). Pero si trato de atraparlo, o establezco una excepción esperada, cualquier código que, después de que se active el error, nunca se ejecute, entonces 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?

¿Fue útil?

Solución

No hay forma de hacer esto dentro de una prueba de unidad. Es posible si termina de probar el valor de retorno y el aviso en dos pruebas diferentes.

El controlador de errores de PHPUnit detecta los 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 en absoluto. Sin embargo, puede deshabilitar temporalmente la conversión de errores en excepciones, incluso en tiempo de ejecución.

Esto es probablemente más fácil con un ejemplo, por lo tanto, así es como deberían verse las dos pruebas:

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 adicional de hacer que sus pruebas sean más claras, limpias y auto documentadas.

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

Entonces, en realidad enmendaría lo anterior para que se vea 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 completamente cierto. Estoy mirando el controlador de errores de PHPUnit, y funciona de la siguiente manera:

  • Si es un E_WARNING , use PHPUnit_Framework_Error_Warning como una clase de excepción.
  • Si se trata de un error E_NOTICE o E_STRICT , use PHPUnit_Framework_Error_Notice
  • De lo contrario, use PHPUnit_Framework_Error como la clase de excepción.

Entonces, sí, los errores del E_USER_ * no se convierten en la clase * _Warning o * _Notice de PHPUnit, todavía se transforman en una excepción genérica PHPUnit_Framework_Error .

Más pensamientos

Si bien depende exactamente de cómo se usa la función, probablemente cambiaría 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 depende de usted decidir si el archivo solicitado que no existe es realmente un 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 su aplicación. Por lo general, reservo los avisos para cosas como llamadas a métodos depreciados, etc.

Otros consejos

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

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

En lugar de esperar una " excepción " ;, genérica, ¿qué hay de esperar una " PHPUnit_Framework_Error " ?

Algo como esto podría hacer:

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

Que, supongo, también podría escribirse 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 obtener más información, consulte Prueba de errores de PHP
Especialmente, dice (citando):

  

PHPUnit_Framework_Error_Notice y    PHPUnit_Framework_Error_Warning representa   Avisos y advertencias de PHP, respectivamente.


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

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

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

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

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

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

Observe que para probar el valor de retorno, debe usar el operador de supresión de errores en la llamada a la función (el @ antes del 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 varias excepciones dentro de una prueba unitaria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top