2009-05-22 10 views
28

Sono per lo più convinto dei vantaggi del test delle unità e vorrei iniziare ad applicare il concetto a una base di codice esistente di grandi dimensioni scritta in PHP. Meno del 10% di questo codice è orientato agli oggetti.Come si scrivono i test unitari in PHP con una base di codice procedurale?

Ho esaminato diversi framework di test delle unità (PHPUnit, SimpleTest e phpt). Tuttavia, non ho trovato esempi per nessuno di questi test del codice procedurale. Qual è il quadro migliore per la mia situazione e ci sono esempi di test di unità PHP che utilizzano codice non OOP?

risposta

34

È possibile testare l'unità PHP procedurale, nessun problema. E non sei assolutamente sfortunato se il tuo codice è mescolato con HTML.

A livello di test di accettazione o applicazione, il PHP procedurale probabilmente dipende dal valore dei superglobali ($_POST, $_GET, $_COOKIE, ecc.) Per determinare il comportamento e termina includendo un file modello e sputando l'output.

Per eseguire test a livello di applicazione, è sufficiente impostare i valori superglobali; avvia un buffer di output (per mantenere un po 'di html dall'inondazione dello schermo); chiama la pagina; asserire contro cose all'interno del buffer; e cestino il buffer alla fine. Quindi, si potrebbe fare qualcosa di simile:

public function setUp() 
{ 
    if (isset($_POST['foo'])) { 
     unset($_POST['foo']); 
    } 
} 

public function testSomeKindOfAcceptanceTest() 
{ 
    $_POST['foo'] = 'bar'; 
    ob_start(); 
    include('fileToTest.php'); 
    $output = ob_get_flush(); 
    $this->assertContains($someExpectedString, $output); 
} 

Anche per enormi "quadri" con un sacco di comprende, questo tipo di test vi dirà se si dispone di funzionalità a livello di applicazione di lavoro o meno. Questo sarà molto importante quando inizierai a migliorare il tuo codice, perché anche se sei convinto che il connettore del database funzioni ancora e abbia un aspetto migliore di prima, dovrai fare clic su un pulsante e vedere che, sì, puoi ancora accedere e uscire tramite il database.

A livelli inferiori, esistono variazioni minori a seconda dell'ambito della variabile e se le funzioni funzionano in base agli effetti collaterali (restituendo true o false) o restituiscono direttamente il risultato.

Le variabili passano esplicitamente, come parametri o matrici di parametri tra le funzioni? O le variabili sono impostate in molti posti diversi e passate implicitamente come globali? Se è il caso (buono) esplicito, è possibile testare una funzione unitaria (1) includendo il file contenente la funzione, quindi (2) inserire direttamente i valori del test funzionale e (3) catturare l'output e asserirlo contro di esso. Se stai usando globals, devi solo fare attenzione (come sopra, nell'esempio $ _POST) per eliminare con attenzione tutti i globals tra i test. È anche particolarmente utile mantenere i test molto piccoli (5-10 linee, 1-2 asserzioni) quando si ha a che fare con una funzione che spinge e tira un sacco di globals.

Un altro problema di base è se le funzioni funzionano restituendo l'output o modificando i parametri passati, restituendo true/false. Nel primo caso, il test è più facile, ma ancora una volta, è possibile in entrambi i casi:

// assuming you required the file of interest at the top of the test file 
public function testShouldConcatenateTwoStringsAndReturnResult() 
{ 
    $stringOne = 'foo'; 
    $stringTwo = 'bar'; 
    $expectedOutput = 'foobar'; 
    $output = myCustomCatFunction($stringOne, $stringTwo); 
    $this->assertEquals($expectedOutput, $output); 
} 

Nel caso in male, in cui il codice funziona da effetti collaterali e restituisce vero o falso, è ancora possibile testare abbastanza facilmente :

/* suppose your cat function stupidly 
* overwrites the first parameter 
* with the result of concatenation, 
* as an admittedly contrived example 
*/ 
public function testShouldConcatenateTwoStringsAndReturnTrue() 
    { 
     $stringOne = 'foo'; 
     $stringTwo = 'bar'; 
     $expectedOutput = 'foobar'; 
     $output = myCustomCatFunction($stringOne, $stringTwo); 
     $this->assertTrue($output); 
     $this->Equals($expectedOutput, $stringOne); 
    } 

Spero che questo aiuti.

+0

Questo non è corretto se il codice che si sta testando è pieno di istruzioni 'exit;' :( –

+3

@YarekT Nessun test può andare bene se il codice è pieno di istruzioni 'exit' o' die'. Il modo più verificabile per Gestire la terminazione prevista dello script è generando un'eccezione e registrando un gestore di eccezioni personalizzato che sia abbastanza intelligente da ignorare il tipo di eccezione personalizzato quando viene rilevato, quindi è possibile verificare che l'eccezione prevista venga generata. Ho mai avuto bisogno di 'exit' o' die' dopo la fase di bootstrap comunque. – rdlowrey

1

Si potrebbe cercare di includere il codice non-oop in una classe di test utilizzando

require_once 'your_non_oop_file.php' # Contains fct_to_test() 

E con PHPUnit si definisce la funzione di test:

testfct_to_test() { 
    assertEquals(result_expected, fct_to_test(), 'Fail with fct_to_test'); 
} 
+0

Questa sembra una soluzione interessante, ma cosa succede se la maggior parte della tua logica non coinvolge funzioni ma invece semplici cicli e condizionali? –

+5

Allora ti sei fatto un grande aiuto per il codice spaghetti. –

+0

Supponevo che Travis non avesse codice php con html. –

6
test

quale unità fanno bene, e ciò che dovresti usarli per, è quando hai un pezzo di codice che dai un certo numero di input, e ti aspetti di ottenere un certo numero di uscite. L'idea è che quando si aggiunge la funzionalità in un secondo momento, è possibile eseguire i test e assicurarsi che continui a eseguire le vecchie funzionalità allo stesso modo.

Quindi, se si dispone di un codice-base procedurale, è possibile realizzare questa chiamata le funzioni nei metodi di prova

require 'my-libraries.php'; 
class SomeTest extends SomeBaseTestFromSomeFramework { 
    public function testSetup() { 
     $this->assertTrue(true); 
    } 

    public function testMyFunction() { 
     $output = my_function('foo',3); 

     $this->assertEquals('expected output',$output); 
    } 
} 

Questo trucco con basi di codice PHP, spesso il vostro codice della libreria interferirà con il funzionamento del framework di test, dato che la base di codice e i framework di test avranno un sacco di codice correlato alla configurazione di un ambiente applicativo in un browser Web (sessione, variabili globali condivise, ecc.). Aspettatevi di passare qualche tempo a raggiungere un punto in cui è possibile includere il codice della libreria ed eseguire un test semplice e sporco (la funzione testSetup sopra).

Se il tuo codice non ha funzioni ed è solo una serie di file PHP che generano pagine HTML, sei piuttosto sfortunato. Il tuo codice non può essere separato in unità distinte, il che significa che l'unità di test non ti sarà molto utile. Faresti meglio a passare il tuo tempo a livello di "test di accettazione" con prodotti come Selenium e Watir. Questi ti consentiranno di automatizzare un browser, quindi di controllare le pagine per i contenuti come posizioni specifiche/nei moduli.

+0

Posso sicuramente separare il mio codice in unità distinte. Il tuo esempio sembra promettente, suppongo che questo non stia usando un particolare framework? –

+0

Sì, questo è solo un pseudo codice di test, ma i veri test SImpleTest e i test PHPUnit sembrano molto simili. –

Problemi correlati