Domanda

Questa domanda è specifica per l'utilizzo di PHPUnit.

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

Esempio di codice da testare:

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 riscontrando è che l'errore innescato causa il fallimento del mio test unitario (come dovrebbe). Ma se provo a rilevarlo o a impostare un'eccezione prevista, qualsiasi codice che dopo l'attivazione dell'errore non viene mai eseguito, quindi non ho modo di testare il valore restituito 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);
}

Qualche idea su come potrei raggiungere questo obiettivo?

È stato utile?

Soluzione

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

Il gestore di errori di PHPUnit rileva errori e notifiche PHP e li converte in Eccezioni, che per definizione interrompe l'esecuzione del programma. La funzione che stai testando non ritorna mai affatto. Puoi, tuttavia, disabilitare temporaneamente la conversione degli errori in eccezioni, anche in fase di esecuzione.

Questo è probabilmente più semplice con un esempio, quindi, ecco come dovrebbero apparire i due test:

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 tuoi test più chiari, più puliti e auto-documentati.

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

Quindi, in realtà modificherei quanto sopra per apparire così:

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

Ri: Secondo commento:

Non è del tutto vero. Sto guardando il 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 E_NOTICE o E_STRICT , utilizzare PHPUnit_Framework_Error_Notice
  • Altrimenti, utilizzare PHPUnit_Framework_Error come classe di eccezione.

Quindi, sì, gli errori dell'opzione E_USER_ * non vengono trasformati nella classe * _Warning o * _Notice di PHPUnit, ma vengono comunque trasformati in un'eccezione PHPUnit_Framework_Error generica.

Ulteriori pensieri

Anche se dipende esattamente da come viene utilizzata la funzione, probabilmente passerei a lanciare un'eccezione effettiva invece di innescare un errore, se fossi in me. Sì, questo cambierebbe il flusso logico del metodo e il codice che utilizza il metodo ... in questo momento l'esecuzione non si interrompe quando non è in grado di leggere un file. Ma sta a te decidere se il file richiesto inesistente è veramente un comportamento eccezionale . Tendo a utilizzare le eccezioni molto più degli errori / avvisi / avvisi, perché sono più facili da gestire, testare e lavorare nel flusso dell'applicazione. Di solito riservo gli avvisi per cose come chiamate di metodo ammortizzate, ecc.

Altri suggerimenti

Utilizza un file di configurazione phpunit.xml e disabilita la conversione di avviso / avviso / errore nella eccezione. Ulteriori dettagli nel manuale . Fondamentalmente è qualcosa del genere:

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

Invece di aspettarti un generico " Eccezione " ;, che dire di aspettarti un " PHPUnit_Framework_Error " ?

Qualcosa del genere 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 ulteriori informazioni, vedere Test degli errori PHP
In particolare, dice (citando):

  

PHPUnit_Framework_Error_Notice e    PHPUnit_Framework_Error_Warning rappresentano   Avvisi e avvisi PHP, rispettivamente.


Guardando il file /usr/share/php/PHPUnit/TextUI/TestRunner.php che ho sul mio sistema, vedo questo (riga 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 un qualche tipo di parametro per attivare quel comportamento? Ma sembra essere abilitato per impostazione predefinita ...

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

Devi solo fare quanto segue:

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 verificare il valore restituito è necessario utilizzare l'operatore di soppressione degli errori nella chiamata di funzione ( @ prima del nome della funzione). In questo modo non verrà generata alcuna eccezione e l'esecuzione continuerà. È quindi necessario impostare l'eccezione prevista come al solito per testare l'errore.

Quello che non puoi fare è testare più eccezioni all'interno di un unit test.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top