2009-08-28 9 views
30

Essenzialmente ho un metodo di una classe chiamato killProgram, che ha lo scopo di inviare un reindirizzamento hTTP e quindi uccidere PHP.Come si usa PHPUnit per testare una funzione se tale funzione dovrebbe uccidere PHP?

Come posso testarlo? Quando eseguo phpunit non restituisce nulla per quel test e si chiude completamente.

In questo momento sto considerando che la funzione killProgram genera un'eccezione che non dovrebbe essere gestita, il che mi consentirà di affermare che è stata generata un'eccezione.

C'è un modo migliore?

risposta

22

Come ogni test vengono eseguiti con lo stesso processo di PHPUnit, se si utilizza l'uscita/die nel codice PHP, potrete uccidere tutto - come notato ^^

Quindi, è necessario trovare un'altra soluzione, sì - come tornare invece di morire; o lanciando un'eccezione (è possibile verificare se qualche codice testato ha generato un'eccezione prevista).

Forse PHPUnit 3.4 ed è --process-isolation interruttore (vedi Optionally execute each test using a separate PHP process) forza aiuto (da non avere tutto ciò che muore), ma ancora non sarebbe in grado di ottenere il risultato del test, se non lo fa PHPUnit riprendere il controllo.

Ho avuto questo problema un paio di volte; risolto tornando invece di morire - anche tornando più volte, se necessario, per tornare "abbastanza alto" nello stack di chiamate ^^
Alla fine, suppongo che non abbia più alcun "dado" nel mio applicazione ... Probabilmente è meglio, quando si pensa a MVC, btw.

+0

Sì, ora ho deciso di lanciare un'eccezione. Il problema è che il mio gestore di eccezioni predefinito ha lo scopo di chiamare questa funzione, quindi devo creare una nuova eccezione chiamata killProgramException che il mio gestore di eccezioni ignora. Tipo di hacking –

7

mi rendo conto che avete già accettato una risposta per questo ed è una vecchia questione, ma immagino che questo potrebbe essere utile per qualcuno, quindi ecco qui:

Invece di usare die(), è possibile utilizzare throw new RuntimeException() (o una tua classe di eccezione), che interromperà anche l'esecuzione del programma (anche se in modo diverso) e userà PHPUnit's setExpectedException() per catturarlo. Se si desidera che lo script sia die() quando si verifica tale eccezione, non si stampa assolutamente nulla a livello dell'utente, dare un'occhiata a set_exception_handler(). In particolare, sto pensando a uno scenario in cui inserire la chiamata set_exception_handler() in un file di bootstrap che i test non utilizzano, quindi il gestore non eseguirà il fuoco lì a prescindere dallo scenario, quindi non interferisce nulla. con la gestione delle eccezioni native di PHPUnit.

+3

Love it when I a old post risponde alla mia domanda. – stefgosselin

+0

questa risposta suggerisce fondamentalmente di utilizzare le eccezioni come strutture di controllo del flusso, che in genere non sono desiderabili. –

13

Non è necessario modificare il codice solo per poterlo testare, è sufficiente utilizzare set_exit_overload() (fornito da test_helpers dallo stesso autore di PHPUnit).

+1

Link per i pigri: https://github.com/php-test-helpers/php-test-helpers (nota che questa è un'estensione PHP che devi attivare nel tuo php.ini) –

+1

Nota: questo progetto è non più mantenuto (dal 2014). – amphetamachine

3

Questo si riferisce a un insieme di problemi che ho ottenuto ricevendo del codice legacy per superare un test. Così mi è venuta in mente una classe testabile come questo ...

class Testable { 
    static function exitphp() { 
     if (defined('UNIT_TESTING')) { 
     throw new TestingPhpExitException(); 
     } else { 
     exit(); 
     } 
    } 
} 

Ora ho semplicemente sostituire le chiamate per uscire() con testabile :: exitphp().

Se è in prova, definisco UNIT_TESTING, in produzione non lo faccio. Sembra un semplice Mock.

+0

IMO, questo è molto elegante. Grazie. –

+1

all'inizio potrebbe sembrare bello, ma questo sta cambiando il comportamento del programma solo per motivi di test, il che non sarebbe bello alla fine IMHO –

16

È ovviamente una vecchia domanda, ma il mio suggerimento sarebbe quello di spostare il codice che è die() in un metodo separato che è quindi possibile prendere in giro.

A titolo di esempio, invece di avere questo:

class SomeClass 
{ 
    public function do() 
    { 
     exit(1); 
     // or 
     die('Message'); 
    } 
} 

fare questo:

class SomeClass 
{ 
    public function do() 
    { 
     $this->terminate(123); 
     // or 
     $this->terminate('Message'); 
    } 

    protected function terminate($code = 0) 
    { 
     exit($code); 
    } 

    // or 
    protected function terminate($message = '') 
    { 
     die($message); 
    } 
} 

In questo modo si può facilmente prendere in giro il metodo terminate e non devi preoccuparti per il script che termina senza che tu sia in grado di catturarlo.

Il test sarebbe simile a questa:

class SomeClassTest extends \PHPUnit_Framework_TestCase 
{ 

    /** 
    * @expectedExceptionCode 123 
    */ 
    public function testDoFail() 
    { 
     $mock = $this->getMock('SomeClass'); 
     $mock->expects($this->any()) 
      ->method('terminate') 
      ->will($this->returnCallback(function($code) { 
       throw new \Exception($code); 
      })); 

     // run to fail 
     $mock->do(); 
    } 
} 

Non ho ancora testato il codice ma dovrebbe essere abbastanza vicino a uno stato funzionante.

Problemi correlati