2009-10-20 26 views
17

Cuando se prueban las excepciones con PHPUnit, ¿cuál es la mejor manera de exigir que cada declaración o afirmación arroje una excepción para que la prueba pase?¿Cómo pruebo las múltiples excepciones con PHPUnit?

Básicamente quiero hacer algo como esto:

public function testExceptions() 
{ 

    $this->setExpectedException('Exception'); 

    foo(-1); //throws exception 
    foo(1); //does not throw exception 

} 

//Test will fail because foo(1) did not throw an exception 

Yo he llegado con lo siguiente, que hace el trabajo, pero es bastante feo OMI.

public function testExceptions() 
{ 

    try { 
     foo(-1); 
    } catch (Exception $e) { 
     $hit = true; 
    } 

    if (!isset($hit)) 
     $this->fail('No exception thrown'); 

    unset($hit); 

    try { 
     foo(1); 
    } catch (Exception $e) { 
     $hit = true; 
    } 

    if (!isset($hit)) 
     $this->fail('No exception thrown'); 

    unset($hit); 

} 

Respuesta

16

Como las excepciones son eventos tan grandes en el flujo del programa, probar varios en una sola prueba es problemático.

Lo más fácil es simplemente dividirlo en dos pruebas: la primera requiere una excepción para poder pasarla, la segunda simplemente se ejecuta y fallaría si arrojara una. Podrías agregar algunas otras pruebas en el segundo si quisieras (confirmando quizás un valor de retorno), pero me inclinaría a asegurarme de que sigue haciendo solo lo esencial, de acuerdo con el nombre. código

/** 
* @expectedException Exception 
*/ 
public function testBadFooThrowsException() 
{ 
    // optional, can also do it from the '@expectedException x' 
    //$this->setExpectedException('Exception'); 
    foo(-1); //throws exception -- good. 
} 

public function testFooDoesNotThrowException() 
{ 
    foo(1); //does not throw exception 
} 
+1

Definitivamente veo su punto, aunque todavía se siente un poco extraño tener múltiples pruebas cuando el objetivo de cada una es asegurar que se lanza una excepción. – etheros

+3

También puede usar la 'anotación @dataProvider' para pasar los valores (e incluso el nombre de una excepción esperada; úselo con '$ this-> setExpectedException ($ x)'). Agregar un nuevo valor de prueba (que lanzaría una excepción) sería simplemente otra entrada de matriz en la función dataProvider. –

+1

Robert Martin diría que siempre dividir casos en diferentes pruebas. En mi opinión, la respuesta @AlisterBulman muestra la solución perfecta para el problema. –

1

Esto no tiene sentido para mí.

Supongo que está tratando de probar varias cosas por separado con un caso de prueba que es una mala práctica.

Cuando foo() arroja la excepción esperada, la prueba es exitosa y bar() no se ejecutará.

Simplemente cree dos casos de prueba separados, que es mucho menos código que el que produjo en el segundo listado.

O explique por qué tendría sentido ejecutar bar(), después de que foo() fallara con una excepción, cuando arrojaría una excepción también.

+0

Estaba tratando de probar llamando a la misma función con diferentes argumentos (y edité mi ejemplo para reflejar mejor esto). Un ejemplo en el manual de PHPUnit muestra múltiples afirmaciones relacionadas que se realizan en una sola prueba, por lo que estaba tratando de replicar esto, pero con excepciones. – etheros

6

un poco más limpio (pero todavía me gustaría sugerir la división de sus pruebas:

try { 
    foo(-1); 
    $this->fail('No exception thrown'); 
} catch (Exception $e) {} 
0

Ampliando la respuesta de @ dave1010, aquí es cómo he resuelto este problema Esto le permite mantener todos estos ". aserciones "ordenadas y ordenadas en una prueba. Simplemente defina una matriz de variables que deberían fallar la prueba, y luego recorra cada una y vea si se produce una excepción. Si falla alguna (sin excepción), la prueba falla, de lo contrario la prueba pasa.

<?php 

public function testSetInvalidVariableType() 
{ 
    $invalid_vars = array(
     '',     // Strings 
     array(),   // Arrays 
     true,    // Booleans 
     1,     // Integers 
     new \StdClass  // Objects 
    ); 

    foreach ($invalid_vars as $var) { 
     try { 
      $object->method($var); 
      $this->fail('No exception thrown for variable type "' . gettype($var) . '".'); 
     } catch (\Exception $expected) { 
     } 
    } 
} 
+0

'fail' arroja' PHPUnit_Framework_AssertionFailedError'. Necesita usar excepciones personalizadas en su código. – sectus

15

Creo que esta es una situación muy común en las pruebas unitarias. El enfoque que uso en estos casos es usando phpunit dataProviders. Todo funciona como se espera, y el código de prueba se vuelve más claro y conciso.

class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function badValues() 
    { 
     return array(
      array(-1), 
      array(1) 
     ); 
    } 


    /** 
    * @dataProvider badValues 
    * @expectedException Exception 
    */ 
    public function testFoo($badValue) 
    { 
     foo($badValue); 
    } 
} 
+2

Muy buena idea ... pruebe muchas excepciones con una prueba. – Andrew

+0

¿Debería 'array (1)' estar ahí aunque? –

+3

esa solución es la más bella y elegante y debe ser la respuesta aceptada. –

Cuestiones relacionadas