2009-08-04 10 views
11

Questa domanda è specifica dell'utilizzo di PHPUnit.verifica il valore di ritorno di un metodo che attiva un errore con PHPUnit

PHPUnit converte automaticamente gli errori di php in eccezioni. Esiste un modo per testare il valore restituito di un metodo che si verifica per attivare un errore php (errori incorporati o errori generati dall'utente tramite trigger_error)?

Esempio di codice per prova:

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

Questo è il tipo di test che voglio scrivere:

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

Il problema che sto avendo è che l'errore innescata provoca il mio test di unità fallire (come dovrebbe). Ma se provo a prenderlo o impostare un'eccezione prevista, qualsiasi codice che dopo l'errore viene attivato non viene mai eseguito, quindi non ho modo di testare il valore di ritorno del metodo.

Questo esempio non funziona:

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

    // code after this point never gets executed 

    $this->assertFalse($result); 
} 

Tutte le idee come avrei potuto raggiungere questo obiettivo?

risposta

21

Non c'è modo di farlo all'interno di un test di unità. È possibile se si interrompe testare il valore restituito e l'avviso in due test diversi.

Il gestore di errori di PHPUnit rileva gli errori e le notifiche PHP e li converte in eccezioni, che per definizione interrompe l'esecuzione del programma. La funzione che stai testando non ritorna mai. Tuttavia, è possibile disabilitare temporaneamente la conversione degli errori in eccezioni, anche in fase di runtime.

Questo è probabilmente più facile con un esempio, così, ecco quello che i due test dovrebbe essere simile:

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

Questo ha anche il vantaggio di rendere i test più chiara, più pulito e di auto documentazione.

Ri: Commento: Questa è una grande domanda, e non avevo idea fino a quando ho eseguito un paio di test. Sembra come se fosse non ripristinare il valore predefinito/originale, almeno a partire da PHPUnit 3.3.17 (l'attuale versione stabile in questo momento).

Quindi, vorrei davvero modificare quanto sopra a guardare in questo modo:

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: secondo commento:

Questo non è completamente vero. Sto guardando gestore degli errori di PHPUnit, e funziona come segue:

  • Se si tratta di un E_WARNING, utilizzare PHPUnit_Framework_Error_Warning come classe di eccezione.
  • Se si tratta di un errore o di E_NOTICEE_STRICT, utilizzare PHPUnit_Framework_Error_Notice
  • Else, utilizzare PHPUnit_Framework_Error come la classe di eccezione.

Quindi, sì, errori del E_USER_* non sono trasformati in * _Warning di PHPUnit o * classe _Notice, sono ancora trasformati in un PHPUnit_Framework_Error un'eccezione generica.

ulteriori riflessioni

Mentre dipende esattamente da come si utilizza la funzione, probabilmente passare a un'eccezione reale, invece di innescare un errore, se si trattasse di me. Sì, questo cambierebbe il flusso logico del metodo e il codice che usa il metodo ... in questo momento l'esecuzione non si ferma quando non può leggere un file. Ma spetta a te decidere se il file richiesto non esistente è veramente comportamento eccezionale. Tendo ad usare le eccezioni molto più di errori/avvisi/notifiche, perché sono più facili da gestire, testare e lavorare nel flusso delle applicazioni. Solitamente riservo le comunicazioni per cose come le chiamate di metodo ammortizzate, ecc.

+0

Grazie jason. Ti capita di sapere se il valore PHPUnit_Framework_Error_Warning :: $ è automaticamente ripristinato tra i test? O è necessario modificarlo manualmente al suo valore originale? – dellsala

+0

Una nota su questa risposta, benché funzioni per errori generati da metodi e metodi php incorporati, non sembra funzionare per i tipi di errore generati dall'utente (E_USER_WARNING e E_USER_NOTICE) trigger_error. Sembra che PHPUnit non supporti l'attivazione di questi in fase di runtime (versione 3.3.17) – dellsala

3

Invece di prevedere un generico "Exception", che dire di un "PHPUnit_Framework_Error" previsto?

Qualcosa di simile potrebbe fare:

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

che, suppongo, potrebbe anche essere scritto come:

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

Per maggiori informazioni, consultare Testing PHP Errors
In particolare, si dice (citando) :

PHPUnit_Framework_Error_Notice e PHPUnit_Framework_Error_Warning rappresentano avvisi e avvertenze PHP, rispettivamente.


Guardando il file /usr/share/php/PHPUnit/TextUI/TestRunner.php ho sul mio sistema, vedo questo (linea 198 e seguenti):

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

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

Quindi forse dovrai passare qualche tipo di parametro per attivare quel comportamento? Ma sembra essere abilitato di default ...

+0

Questo non è quello che stava chiedendo. – jason

+0

jason ha ragione - non esattamente quello che stavo chiedendo, ma un buon consiglio su come verificare lo specifico tipo di errore php usando i tipi di eccezione PHPUnit. – dellsala

+0

@jason: oh, la tua destra :-(scusa :-(Bella risposta, btw (il fatto di usare due test separati è sicuramente un buon punto) @dellsala: grazie :-) –

8

Utilizzare un file di configurazione phpunit.xml e disabilitare la notifica/avviso/errore nella conversione Eccezione. Più details in the manual. Praticamente è qualcosa del genere:

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

@lonut - stavo cercando _questo ! Post di 2 anni, ancora pertinente – stefgosselin

1

In realtà esiste un modo per testare sia il valore di ritorno sia l'eccezione generata (in questo caso un errore convertito da PHPUnit).

Non vi resta che effettuare le seguenti operazioni:

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

Si noti che per testare per il valore di ritorno si deve usare l'operatore di soppressione errore la chiamata di funzione (la @ prima del nome della funzione). In questo modo non verrà lanciata alcuna eccezione e l'esecuzione continuerà.È quindi necessario impostare come al solito l'eccezione prevista per verificare l'errore.

Ciò che non si può fare è testare più eccezioni all'interno di un test unitario.

Problemi correlati