Fork me on GitHub Looking for a PHP Development job using Symfony2 to build a cutting edge web application - check out DeskPRO Jobs

«  [Part 5] - Customising the view: Twig extensions, The sidebar and Assetic   ::   Contents

[Part 6] - Testing: Unit and Functional with PHPUnit

Panoramica

Finora abbiamo esplorato una buona quantità di terreno esaminando una serie di concetti fondamentali per quanto riguarda lo sviluppo di Symfony2 Prima di continuare l'aggiunta di funzionalità è il momento di introdurre il test. Vedremo come testare singole funzioni con unit test e su come garantire che i componenti stiano lavorando correttamente con i test funzionali.E' con la libreria test PHP PHPUnit che copriremo i test al centro di Symfony2 .Sicome test è un argomento vasto questi saranno trattati anche nei capitoli successivi. Entro la fine di questo capitolo avrete scritto un certo numero di test che coprono sia unit che test funzionali. Avremo le richieste del browser, form simulati popolati con i dati e il controllo delle risposte per garantire che le pagine del sito abbiano l'output in modo corretto. Avrete anche verificato quanta copertura avete con i test sul vostro codice di base dell' applicazione.

Test in Symfony2

PHPUnit è diventato il "de facto standard" per la scrittura di test in PHP, in modo da imparare per usufruirne, in tutti i vostri progetti PHP. Inoltre, non dimentichiamo che la maggior parte degli argomenti trattati in questo capitolo sono indipendenti dal linguaggio e quindi possono essere trasferite ad altri linguaggi .

Nota

Se state pianificando di scrivere i vostri bundles Symfony2 open source, è molto più probabile di ricevere degli interessi se il bundle è ben testato (e documentato). Dai un'occhiata a quelli esistenti di Symfony2 disponibili a Symfony2Bundles .

Unit Testing

Unit testing si occupa di garantire che le funzioni di singole unità di codice funzionino correttamente quando vengono utilizzate in modo isolato. In una base di codice Object Oriented, come Symfony2, una unità sarebbe una classe e i suoi metodi. Ad esempio, potremmo scrivere dei test per le classi di entità di Blog e commenti . Durante la scrittura di unit test, i test devono essere scritti indipendentemente da altri casi di test, vale a dire, il risultato di test case B non dovrebbe dipendere dal risultato di test case A. E 'utile in quanto unit test può essere in grado di creare oggetti mock che consentono di provare facilmente le funzioni di unit test che hanno dipendenze esterne. Mocking consente di simulare una chiamata di funzione, invece di esecutarla effettivamente . Un esempio di questo potrebbe essere una classe unit test che chiama una API esterno. La classe API può utilizzare unlivello di trasporto per comunicare con l'API esterna.Con mock potremmo prendere il metodo di richiesta del livello di trasporto per restituire i risultati precisi, piuttosto che in realtà usare l'API esterna. Unit testing può verificare che i componenti di un'applicazione funzionino correttamente insieme, questo è coperto da l'argomento successivo, collaudo funzionale.

Testing Funzionale

Il Test di verifica funzionale controlla l'integrazione di diverse componenti all'interno dell'applicazione, come il routing, controller e le viste. I test funzionali sono simili ai test manuali che saresti ad eseguire nel browser, come richiede la homepage, cliccando sul link blog e controllando se il blog corretto è indicato. Il collaudo funzionale vi offre la possibilità di automatizzare questo processo. Symfony2 viene fornito con una serie di classi utili che aiutano con i test funzionali tra cui un client in grado dirichiedere pagine e presentare form e DOM Crawler che possiamo usare per attraversare la risposta da parte del cliente.

Nota

Ci sono un certo numero di software di sviluppo che sono driven by testing. Questi includono i processi come il Test Driven Development (TDD) e comportamentali Driven Development (BDD). Mentre questi sono fuori della portata di questa esercitazione si deve essere consapevoli della libreria scritta da everzet che facilita BDD chiamato Béhat . C'è anche un Symfony2 BehatBundle a disposizione per integrare facilmente Béhat in un progetto Symfony2.

PHPUnit

Come detto sopra,I test di Symfony2 sono scritti utilizzando PHPUnit. Avrete bisogno di installare PHPUnit al fine di eseguire questi test e le prove da questo capitolo. Per informazioni dettagliate istruzioni di installazione fare riferimento alla documentazione ufficiale sul sito PHPUnit. Per eseguire i test in Symfony2 è necessario installare PHPUnit 3.5.11 o successivo. PHPUnit è una libreria di test molto grande, quindi i riferimenti alla documentazione ufficiale verrà effettuato per trovare una lettura aggiuntiva.

Asserzioni

Test di scrittura concernenti a verificare che il risultato del test reale è pari al risultato del test atteso. Ci sono un certo numero di metodi di affermazione disponibili in PHPUnit per aiutarvi in ​​questo compito. Alcuni dei metodi affermazione comuni che verranno utilizzati sono elencati di seguito.

// Check 1 === 1 is true
$this->assertTrue(1 === 1);

// Check 1 === 2 is false
$this->assertFalse(1 === 2);

// Check 'Hello' equals 'Hello'
$this->assertEquals('Hello', 'Hello');

// Check array has key 'language'
$this->assertArrayHasKey('language', array('language' => 'php', 'size' => '1024'));

// Check array contains value 'php'
$this->assertContains('php', array('php', 'ruby', 'c++', 'JavaScript'));

Una lista completa delle asserzioni è disponibile nella documentazione PHPUnit.

Esecuzione di test Symfony2

Prima di iniziare a scrivere qualche test, andiamo ad esaminare come eseguire i test in Symfony2. PHPUnit può essere impostato per eseguire questo utilizzando un file di configurazione. Nel nostro progetto Symfony2 questo file si trova in app / phpunit.xml.dist . Dato che questo file ha il suffisso . dist , è necessario copiare il contenuto in un file chiamato app / phpunit.xml .

 

Nota

Se si utilizza un VCS come Git, si dovrebbe aggiungere il nuovo app / phpunit.xml file ai VCS lista ignora.

Se si da uno sguardo al contenuto del file di configurazione PHPUnit verrà visualizzato il seguente.

<!-- app/phpunit.xml -->

<testsuites>
    <testsuite name="Project Test Suite">
        <directory>../src/*/*Bundle/Tests</directory>
        <directory>../src/*/Bundle/*Bundle/Tests</directory>
    </testsuite>
</testsuites>

Le seguenti impostazioni configurano alcune directory che fanno parte della nostra suite di test. Quando si esegue PHPUnit cercherà nelle directory di cui sopra per i test da eseguire. È inoltre possibile passare ulteriori argomenti dalla riga di comando per far eseguire a PHPUnit i test in directory specifiche, anziché fare le prove nella suite di test. Vedrete come raggiungere questo obiettivo più tardi.

Noterete anche che la configurazione specifica il file di bootstrap in app / bootstrap.php.cache . Questo file è usato da PHPUnit per ottenere la configurazione dell'ambiente di test.

<!-- app/phpunit.xml -->

<phpunit
    bootstrap                   = "bootstrap.php.cache" >

Nota

Per ulteriori informazioni sulla configurazione di PHPUnit con un file XML vedere la documentazione PHPUnit

 

Esecuzione dei test attuali

Sicome abbiamo utilizzato uno dei task di i Symfony2 per creare il BloggerBlogBundle indietro nel capitolo 1, questo ha anche creato un test di controllo per la classe DefaultController.Cosi siamo in grado di eseguire questo test eseguendo il comando seguente dalla directory principale del progetto. Il c- opzione specifica che PHPUnit deve caricare la configurazione dal app directory.

$ phpunit -c app

Una volta che il test è completato si i deve essere informati che il test non è riuscito. Se si guarda alla classe DefaultControllerTest in src / Blogger / BlogBundle / Test / controller / DefaultControllerTest.php verrà visualizzato il seguente contenuto.

<?php
// src/Blogger/BlogBundle/Tests/Controller/DefaultControllerTest.php

namespace Blogger\BlogBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class DefaultControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', '/hello/Fabien');

        $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
    }
}

Questo è un test funzionale per la classe DefaultController che Symfony2 ha generato. Se vi ricordate il capitolo 1, questo controller ha un 'azione che ha gestito le richieste a / ciao / {nome} . Il fatto che abbiamo rimosso questo controller è il motivo per cui il test sta fallendo. Provate ad andare all'URL http://symblog.dev/app_dev.php/hello/Fabien nel tuo browser. Si devono essere informati che il percorso non è stato trovato. Come prova di cui sopra facciamo una richiesta allo stesso URL, che avrà la stessa risposta, quindi, questo è perché il test ha esito negativo.I Testing funzionali sono una grande parte di questo capitolo, saranno coperti in dettaglio più avanti.

Sicome la classe DefaultController è stata rimossa, è anche possibile rimuovere questa classe dai test. Eliminare la classe DefaultControllerTest in src / Blogger / BlogBundle / Test / controller / DefaultControllerTest.php .

Unit Testing

Come spiegato in precedenza, unit test si occupa di testare le singole unità della vostra applicazione . Durante la scrittura di unit test si consiglia di replicare la struttura del Bundle nella cartella test. Ad esempio, se si vuole testare la classe dell'entità Blog in src / Blogger / BlogBundle / Entità / Blog.php il file di prova dovrebbe essere in src / Blogger / BlogBundle / Test / Entità / BlogTest.php . Un layout di esempio della cartella potrebbe essere il seguente.

src/Blogger/BlogBundle/
                Entity/
                    Blog.php
                    Comment.php
                Controller/
                    PageController.php
                Twig/
                    Extensions/
                        BloggerBlogExtension.php
                Tests/
                    Entity/
                        BlogTest.php
                        CommentTest.php
                    Controller/
                        PageControllerTest.php
                    Twig/
                        Extensions/
                            BloggerBlogExtensionTest.php

Si noti che ciascuno dei file di prova sono con suffisso test.

Test del Blog Entity - metodo slugify

Iniziamo analizzando il metodo slugify nell'entità Blog. Andiamo a scrivere alcuni test per assicurare che questo metodo funziona correttamente. Creare un nuovo file in src / Blogger / BlogBundle / Test / Entità / BlogTest.php e aggiungere quanto segue.

<?php
// src/Blogger/BlogBundle/Tests/Entity/BlogTest.php

namespace Blogger\BlogBundle\Tests\Entity;

use Blogger\BlogBundle\Entity\Blog;

class BlogTest extends \PHPUnit_Framework_TestCase
{

}

Abbiamo creato una classe di test per le entitàl Blog. Si noti la posizione del file è conforme alla struttura delle cartelle di cui sopra. La classe BlogTest estende la classe di base PHPUnit PHPUnit_Framework_TestCase . Tutte le prove scritte per PHPUnit saranno figlio di questa classe. Ti ricorderai dai capitoli precedenti che la \ deve essere collocato davanti al nome della classe PHPUnit_Framework_TestCase sic come la classe viene dichiarata nel namespace PHP pubblico.

Ora abbiamo la classe skeleton per i nostri test blog entità, andiamo a scrivere un insieme di test. questi insiemi di test in PHPUnit sono metodi della classe di test con prefisso test , come testSlugify () . Aggiornare il BlogTest in src / Blogger / BlogBundle / Test / Entità / BlogTest.php con il seguente.

// src/Blogger/BlogBundle/Tests/Entity/BlogTest.php

// ..

class BlogTest extends \PHPUnit_Framework_TestCase
{
    public function testSlugify()
    {
        $blog = new Blog();

        $this->assertEquals('hello-world', $blog->slugify('Hello World'));
    }
}

Si tratta di un test di prova molto semplice. Si crea un'istanza di una nuova entità Blog che gestisce un assertEquals () sul risultato del

metodo slugify. Il metodo assertEquals () richiede 2 argomenti obbligatori, il risultato atteso e il risultato effettivo. Un terzo argomento opzionale può essere passato per specificare un messaggio da visualizzare quando il test fallisce.

Andiamo ad eseguire il nostro nuovo test . Eseguire la seguente riga di comando.

$ phpunit -c app

Si dovrebbe vedere il seguente output.

PHPUnit 3.5.11 by Sebastian Bergmann.

.

Time: 1 second, Memory: 4.25Mb

OK (1 test, 1 assertion)

L'uscita dal PHPUnit è molto semplice, inizia visualizzando alcune informazioni PHPUnit e le uscite di un certo numero . per ogni test che viene eseguito, nel nostro caso abbiamo solo 1 test in esecuzione in modo che solo uno . è in uscita. L'ultima affermazione ci informa del risultato dei test. Per il nostro BlogTest abbiamo eseguito solo 1 test con 1 affermazione. Se si dispone di output a colori sulla linea di comando si vedrà anche l'ultima riga visualizzata in verde seguito da OK. Andiamo ad aggiornare il metodo testSlugify () per vedere cosa succede quando i test fallisce.

 

// src/Blogger/BlogBundle/Tests/Entity/BlogTest.php

// ..

public function testSlugify()
{
    $blog = new Blog();

    $this->assertEquals('hello-world', $blog->slugify('Hello World'));
    $this->assertEquals('a day with symfony2', $blog->slugify('A Day With Symfony2'));
}

Ri esecuzione degli unit test come prima. L'uscita verrà visualizzata nel modo seguente

PHPUnit 3.5.11 by Sebastian Bergmann.

F

Time: 0 seconds, Memory: 4.25Mb

There was 1 failure:

1) Blogger\BlogBundle\Tests\Entity\BlogTest::testSlugify
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-a day with symfony2
+a-day-with-symfony2

/var/www/html/symblog/symblog/src/Blogger/BlogBundle/Tests/Entity/BlogTest.php:15

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

L'uscita è un po diversa questa volta. Possiamo vedere . per le prove di esecuzione è sostituito da un F . Questo ci dice il test è fallito. Si vedrà anche la E l'output di caratteri se il test contiene degli errori. PHPUnit successivamente ci notifica in dettaglio gli errori, in questo caso, il 1 errore. Possiamo vedere il metodo Blogger \ BlogBundle \ test \ Entity \ BlogTest :: testSlugify che è riuscito perché i valori attesi e effettivi erano diversi. Se si dispone di output a colori sulla linea di comando si vedrà l'ultima riga visualizzata in rosso, mostra l'esistenza di fallimenti nei test. Correggere il metodo testSlugify () in modo che i test eseguiti siano corretti.

// src/Blogger/BlogBundle/Tests/Entity/BlogTest.php

// ..

public function testSlugify()
{
    $blog = new Blog();

    $this->assertEquals('hello-world', $blog->slugify('Hello World'));
    $this->assertEquals('a-day-with-symfony2', $blog->slugify('A Day With Symfony2'));
}

Prima di passare il test andiamo ad aggiungere ancora qualcosa per il metodo slugify ().

// src/Blogger/BlogBundle/Tests/Entity/BlogTest.php

// ..

public function testSlugify()
{
    $blog = new Blog();

    $this->assertEquals('hello-world', $blog->slugify('Hello World'));
    $this->assertEquals('a-day-with-symfony2', $blog->slugify('A Day With Symfony2'));
    $this->assertEquals('hello-world', $blog->slugify('Hello    world'));
    $this->assertEquals('symblog', $blog->slugify('symblog '));
    $this->assertEquals('symblog', $blog->slugify(' symblog'));
}

Ora abbiamo testato il metodo slugify per l'entità Blog , abbiamo bisogno di garantire che il membro Blog $ slug sia impostato correttamente quando il membro $ title del Blog viene aggiornato. Aggiungere i seguenti metodi al file BlogTest in src / Blogger / BlogBundle / Test / Entità / BlogTest.php .

// src/Blogger/BlogBundle/Tests/Entity/BlogTest.php

// ..

public function testSetSlug()
{
    $blog = new Blog();

    $blog->setSlug('Symfony2 Blog');
    $this->assertEquals('symfony2-blog', $blog->getSlug());
}

public function testSetTitle()
{
    $blog = new Blog();

    $blog->setTitle('Hello World');
    $this->assertEquals('hello-world', $blog->getSlug());
}

Cominciamo testando il metodo setSlug per garantire che il membro $ slug è slugified correttamente dopo l'aggiornamento. Poi andiamo a controllare che il membro $ slug venga aggiornato correttamente quando il metodo setTitle viene chiamato sull'entità Blog.

Eseguire i test per verificare che l'entità l Blog funziona correttamente.

Test per una estensione Twig

Nel capitolo precedente abbiamo creato un estensione di Twig per convertire un DateTime \ in una stringa che specificava la durata del tempo passato. Creare un nuovo file di prova in src / Blogger / BlogBundle / Test / Twig / Extensions / BloggerBlogExtensionTest.php e aggiornare con il seguente contenuto.

<?php
// src/Blogger/BlogBundle/Tests/Twig/Extensions/BloggerBlogExtensionTest.php

namespace Blogger\BlogBundle\Tests\Twig\Extensions;

use Blogger\BlogBundle\Twig\Extensions\BloggerBlogExtension;

class BloggerBlogExtensionTest extends \PHPUnit_Framework_TestCase
{
    public function testCreatedAgo()
    {
        $blog = new BloggerBlogExtension();

        $this->assertEquals("0 seconds ago", $blog->createdAgo(new \DateTime()));
        $this->assertEquals("34 seconds ago", $blog->createdAgo($this->getDateTime(-34)));
        $this->assertEquals("1 minute ago", $blog->createdAgo($this->getDateTime(-60)));
        $this->assertEquals("2 minutes ago", $blog->createdAgo($this->getDateTime(-120)));
        $this->assertEquals("1 hour ago", $blog->createdAgo($this->getDateTime(-3600)));
        $this->assertEquals("1 hour ago", $blog->createdAgo($this->getDateTime(-3601)));
        $this->assertEquals("2 hours ago", $blog->createdAgo($this->getDateTime(-7200)));

        // Cannot create time in the future
        $this->setExpectedException('\InvalidArgumentException');
        $blog->createdAgo($this->getDateTime(60));
    }

    protected function getDateTime($delta)
    {
        return new \DateTime(date("Y-m-d H:i:s", time()+$delta));
    }
}

L'impostazione della classe è più o meno la stessa di prima, creando un metodo testCreatedAgo () per testare la Twig Extension. Vi presentiamo un altro metodo PHPUnit in questo caso un test per il metodo setExpectedException (). Questo metodo deve essere chiamato prima che l'esecuzione del metodo possa generare un'eccezione. Sappiamo che il metodo createdAgo della Twig estensione non può gestire date in futuro e genererà un Exception \ . Il metodo getDateTime () è semplicemente un metodo di supporto per la creazione di un esempio \ DateTime. Si noti che non è preceduto da test in modo PHPUnit non cercherà di eseguirlo come un metodo di test . Aprire la riga di comando ed eseguire i test per questo file. Si potrebbe semplicemente eseguire il test come prima, ma possiamo anche dire a PHPUnit di eseguire i test per una cartella specifica (e relative sottocartelle) o un file. Eseguire il comando seguente.

$ phpunit -c app src/Blogger/BlogBundle/Tests/Twig/Extensions/BloggerBlogExtensionTest.php

Questo farà eseguire i test per il solo file BloggerBlogExtensionTest. PHPUnit ci informa che i test non sono riusciti. L'output è mostrato di seguito.

1) Blogger\BlogBundle\Tests\Twig\Extension\BloggerBlogExtensionTest::testCreatedAgo
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-0 seconds ago
+0 second ago

/var/www/html/symblog/symblog/src/Blogger/BlogBundle/Tests/Twig/Extensions/BloggerBlogExtensionTest.php:14

Ci aspettavamo che la prima affermazione restituisse 0 secondi fa , ma non l'ha fatto, la seconda parola non è un plurale. Andiamo ad aggiornare la Twig Extension in src / Blogger / BlogBundle / Twig / Extensions / BloggerBlogBundle.php per correggere questo

<?php
// src/Blogger/BlogBundle/Twig/Extensions/BloggerBlogBundle.php

namespace Blogger\BlogBundle\Twig\Extensions;

class BloggerBlogExtension extends \Twig_Extension
{
    // ..

    public function createdAgo(\DateTime $dateTime)
    {
        // ..
        if ($delta < 60)
        {
            // Seconds
            $time = $delta;
            $duration = $time . " second" . (($time === 0 || $time > 1) ? "s" : "") . " ago";
        }
        // ..
    }

    // ..
}

Eseguire nuovamente i test PHPUnit. Si dovrebbe vedere la prima affermazione che passa correttamente, ma il nostro insieme di test non riesce ancora. Andiamo ad esaminare l'uscita successiva.

1) Blogger\BlogBundle\Tests\Twig\Extension\BloggerBlogExtensionTest::testCreatedAgo
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-1 hour ago
+60 minutes ago

/var/www/html/symblog/symblog/src/Blogger/BlogBundle/Tests/Twig/Extensions/BloggerBlogExtensionTest.php:18

Ora possiamo vedere che l'affermazione 5 sta fallendo (si noti il 18 al termine del output , questo ci dà il numero di riga del file in cui l'asserzione non è riuscita). Guardando l'insieme di test , possiamo vedere che la Twig Extension non ha funzionato correttamente. Sarebbe dovuto uscire 1 ora fa , ma invece è uscito 60 minuti fa. Se esaminiamo il codice nel BloggerBlogExtension Twig estensione possiamo vedere la ragione. Confrontiamo il tempo per essere inclusivo, cioè, usiamo <= invece di < . Possiamo vedere che questo è il caso anche per il controllo delle ore. Aggiornare la Twig estensione in src / Blogger / BlogBundle / Twig / Extensions / BloggerBlogBundle.php per correggere questo.

<?php
// src/Blogger/BlogBundle/Twig/Extensions/BloggerBlogBundle.php

namespace Blogger\BlogBundle\Twig\Extensions;

class BloggerBlogExtension extends \Twig_Extension
{
    // ..

    public function createdAgo(\DateTime $dateTime)
    {
        // ..

        else if ($delta < 3600)
        {
            // Mins
            $time = floor($delta / 60);
            $duration = $time . " minute" . (($time > 1) ? "s" : "") . " ago";
        }
        else if ($delta < 86400)
        {
            // Hours
            $time = floor($delta / 3600);
            $duration = $time . " hour" . (($time > 1) ? "s" : "") . " ago";
        }

        // ..
    }

    // ..
}

Ora nuovamente eseguire tutti i test utilizzando il seguente comando

 

$ phpunit -c app

Questo eseguirà tutti i nostri test, e mostra che tutti i test passano con successo. Anche se abbiamo scritto solo un piccolo numero di unit test si dovrebbe ottenere un idea di come i test siano potenti e importanti quando si scrive codice. Mentre gli errori di cui sopra sono stati trascurabili, erano piccoli errori. Testing ci aiuta anche per qualsiasi funzionalità futura si aggiunga al progetto interrompendo funzionalità precedenti. Questo conclude la unit testing, per ora. Vedremo unit test più a fondo nei capitoli seguenti. Provate ad aggiungere alcuni dei vostri unit test per proprie funzionalità .

Testing Funzionale

Abbiamo scritto finora test unitari, permette di passare alla sperimentazione di più componenti insieme. La prima sezione del collaudo funzionale comporterà la simulazione di richieste di browser per testare le risposte generate.

Test della pagina About

Iniziamo con il testare la classe PageController per la pagina About. Poiché la pagina About è molto semplice, questo è un buon punto di partenza. Creare un nuovo file in src / Blogger / BlogBundle / Test / controller / PageControllerTest.php e aggiungere il seguente contenuto.

<?php
// src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

namespace Blogger\BlogBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class PageControllerTest extends WebTestCase
{
    public function testAbout()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', '/about');

        $this->assertEquals(1, $crawler->filter('h1:contains("About symblog")')->count());
    }
}

Abbiamo già visto un test di controllo molto simile a questo quando abbiamo brevemente esaminato la classe DefaultControllerTest. Questo sta testando la pagina di symblog about, controllando che la stringa about symblog sia presente nel codice HTML generato, in particolare all'interno del tag H1. La classe PageControllerTest non estende l' \ PHPUnit_Framework_TestCase come abbiamo visto con gli esempi di test unitari, si estende invece la classe WebTestCase . Questa classe è parte del FrameworkBundle Symfony2.

Come spiegato prima dell'inizio delle lezioni di prova PHPUnit deve estendere la \ PHPUnit_Framework_TestCase , ma quando la funzionalità extra o comune sono necessarie in casi di test a risposta multipla è utile incapsulare questo nella sua classe e avere le classi di test che estendono questo. Il WebTestCase fa esattamente questo, fornisce una serie di metodi utili per l'esecuzione di prove funzionali in Symfony2. Dai un'occhiata al file WebTestCase in vendor / symfony / src / Symfony / Bundle / FrameworkBundle / Test / WebTestCase.php per vedrete che questa classe estende la classe \ PHPUnit_Framework_TestCase.

// vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php

abstract class WebTestCase extends \PHPUnit_Framework_TestCase
{
    // ..
}

Se si da un occhiata al metodo createClient () nella classe WebTestCase si può vedere che crea un'istanza del Kernel Symfony2. Seguendo i metodi noterete anche che lo sviluppo è impostato a test (a meno che non sia sovrascritto come uno degli argomenti createClient () ). Questa è l'ambiente test del quale abbiamo parlato nel capitolo precedente.

Guardando indietro alla nostra classe di test si può vedere il createClient () dove viene chiamato il metodo per ottenere il test attivo e funzionante. Dobbiamo quindi chiamare il metodo request () sul client per simulare che un browser richieda HTTP GET all'URL / about (questo sarebbe proprio come quando si visita http://symblog.dev/about nel proprio browser). La richiesta ci dà indietro un oggetto Crawler , che contiene la response . La classe Crawler è molto utile in quanto ci permette di attraversare il codice HTML restituito. Usiamo il Crawler , per verificare cheil tag ' H1 nel codice HTML di risposta contenga le parole about symblog . Noterete che, anche se stiamo estendendo la classe WebTestCase usiamo ancora il metodo Assert come prima (ricordate la classe PageControllerTest è ancora figlio della classe \ PHPUnit_Framework_TestCase ).

Andiamo ad eseguire il PageControllerTest utilizzando il seguente comando. Questo è usato solo per eseguire i test per il file che si sta attualmente controllando . Man mano che la suite di test diventa grande ,questa prova di funzionamento può diventare onerosa.

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

Si dovrebbe ottenere il messaggio OK (1 test, 1 affermazione) farci sapere che 1 test (il testAbout () ) ha funzionato, con 1 affermazione ( assertEquals () ).

Provare a cambiare la stringa about symblog con contat e poi nuovamente eseguite il test. Il test avrà esito negativo in quanto Contact non può essere trovato, causando asertEquals equiparato a false.

1) Blogger\BlogBundle\Tests\Controller\PageControllerTest::testAbout
Failed asserting that 0 matches expected 1.

Ripristinare la stringa a about symblog prima di continuare.

L'esempio Crawler utilizzato permette di attraversare sia i documenti HTML o XML (che significa che il crawler funziona solo con le risposte che restituiscono HTML o XML). Possiamo usare il crawler per attraversare la risposta generata utilizzando metodi quali filter () , first () , last () , e parents () . Se avete usato jQuery prima ci si dovrebbe sentire a casa con la classe Crawler. Un elenco completo dei metodi supportati da crawler di attraversamento può essere trovato nel capitolo Testing del libro Symfony2. Esploreremo più su le caratteristiche dei crowler , mentre continuiamo.

Homepage

Mentre il test per la pagina about era semplice, ha delineato i principi fondamentali del collaudo funzionale nelle pagine del sito.

  1. Creare il client
  2. Richiesta di una pagina
  3. Controllare la risposta

Si tratta di una semplice panoramica del processo, in realtà ci sono un certo numero di altri passi che potremmo fare, come fare clic su collegamenti e popolamento di form per la presentazione.

Andiamo a creare un metodo per testare la homepage. Sappiamo che la homepage è disponibile tramite l'URL / e chei dovrebbe visualizzare i post più recenti del blog. Aggiungere un nuovo metodo di testIndex () alla classe PageControllerTest in src / Blogger / BlogBundle / Test / controller / PageControllerTest.php come illustrato di seguito.

// src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

public function testIndex()
{
    $client = static::createClient();

    $crawler = $client->request('GET', '/');

    // Check there are some blog entries on the page
    $this->assertTrue($crawler->filter('article.blog')->count() > 0);
}

Potete vedere gli stessi passaggi come con i test per la pagina About. Esegui il test per garantire che tutto funzioni come previsto.

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

Parte del testing funzionale comporta di essere in grado di replicare ciò che un utente avrebbe fatto sul sito. Per consentire agli utenti di spostarsi tra le pagine del tuo sito web come fare clic sui link. Andiamo a simulare questa azione ora per verificare che i collegamenti per la pagina show blog lavorino correttamente quando il titolo del blog è stato cliccato. Aggiornare il metodo testIndex () nella classe PageControllerTest con il seguente.

// src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

public function testIndex()
{
    // ..

    // Find the first link, get the title, ensure this is loaded on the next page
    $blogLink   = $crawler->filter('article.blog h2 a')->first();
    $blogTitle  = $blogLink->text();
    $crawler    = $client->click($blogLink->link());

    // Check the h2 has the blog title in it
    $this->assertEquals(1, $crawler->filter('h2:contains("' . $blogTitle .'")')->count());
}

La prima cosa ,che facciamo utilizziamo il Crawler per estrarre il testo all'interno del primo link del titolo blog. Questo viene fatto usando il filtroarticle.blog h2 a. Questo filtro è utilizzato per far ritornare il tag a all'interno del tag H2 dell'articolo article.blog .Per capire meglio questo, diamo uno sguardo al markup utilizzato sulla home page per la visualizzazione blog.

<article class="blog">
    <div class="date"><time datetime="2011-09-05T21:06:19+01:00">Monday, September 5, 2011</time></div>
    <header>
        <h2><a href="/app_dev.php/1/a-day-with-symfony2">A day with Symfony2</a></h2>
    </header>

    <!-- .. -->
</article>
<article class="blog">
    <div class="date"><time datetime="2011-09-05T21:06:19+01:00">Monday, September 5, 2011</time></div>
    <header>
        <h2><a href="/app_dev.php/2/the-pool-on-the-roof-must-have-a-leak">The pool on the roof must have a leak</a></h2>
    </header>

    <!-- .. -->
</article>

Possiamo vedere il filtro article.blog h2 a a posto nella struttura markup della homepage . Noterete anche che c'è più di un<article class="blog"> nel markup, cioè il filtro Crawler restituirà una collezione. Sicome vogliamo solo il primo link , si usa il metodo first () su la collezione . Infine, si usa il metodo text () per estrarre il testo del link, in questo caso sarà il testo Un giorno con Symfony2 . In seguito, il link del titolo blog si fa clic per accedere alla pagina show blog. Il metodo client click () prende un oggetto e restituisce il collegamento di risposta in un esempio Crawler. Si dovrebbe ormai aver notando che l'oggetto Crawler è un elemento fondamentale al collaudo funzionale.onal testing.

L'oggetto Crawler contiene ora la risposta per la pagina show blog. Abbiamo ora bisogno di verificare il link che ci ha navigato alla pagina giusta. Possiamo usare il valore $ BlogTitle recuperato in precedenza per verificare questo confrontandolo con il titolo nella risposta.

Eseguire i test per assicurare che la navigazione tra la home page e le pagine show blog funziona correttamente.

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

Now you have an understanding of how to navigate through the website pages when functional testing, lets move onto testing forms.

Test della pagina Contact

Gli utenti di symblog sono in grado di inviare richieste di contatto compilando il form nella pagina contatti http://symblog.dev/contact . Andiamo a testare che questo modulo funziona correttamente. Per prima cosa dobbiamo delineare quello che dovrebbe accadere quando il modulo viene inviato correttamente (presentato con successo in questo caso significa che non ci sono errori presenti nel form).

  1. Passare alla pagina contatti
  2. Compilare il form di contatto con dei valori
  3. Invio del form
  4. Controllare la posta elettronica se è stato inviato a symblog
  5. Controllare se la risposta al client contiene la notifica di invio con successo

Fino ad ora abbiamo sperimentato abbastanza per essere in grado di completare i passaggi 1 e 5 da soli. Ora vedremo in che modo testare i 3 passi intermedi.

Aggiungere un nuovo metodo di testContact () alla classe PageControllerTest in src / Blogger / BlogBundle / Test / controller / PageControllerTest.php .

// src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

public function testContact()
{
    $client = static::createClient();

    $crawler = $client->request('GET', '/contact');

    $this->assertEquals(1, $crawler->filter('h1:contains("Contact symblog")')->count());

    // Select based on button value, or id or name for buttons
    $form = $crawler->selectButton('Submit')->form();

    $form['blogger_blogbundle_enquirytype[name]']       = 'name';
    $form['blogger_blogbundle_enquirytype[email]']      = 'email@email.com';
    $form['blogger_blogbundle_enquirytype[subject]']    = 'Subject';
    $form['blogger_blogbundle_enquirytype[body]']       = 'The comment body must be at least 50 characters long as there is a validation constrain on the Enquiry entity';

    $crawler = $client->submit($form);

    $this->assertEquals(1, $crawler->filter('.blogger-notice:contains("Your contact enquiry was successfully sent. Thank you!")')->count());
}

Si inizia nel modo consueto, facendo una richiesta al URL / contact , e controllando se la pagina contiene il corretto titolo H1. Poi si usa il Crawler per selezionare il pulsante per inviare il form. Il motivo per cui selezioniamo il pulsante e non il form è che un form può contenere più pulsanti che si potrebbe desiderare di scegliere in modo indipendente. Dal pulsante selezionato siamo in grado di recuperare il form . Siamo in grado di impostare i valori del form usando l'array subscript notation[] . Infine, il form viene passato al client con il metodo submit () per effettivamente presentare il modulo. Come al solito, riceviamo indietro un istanza Crawler.E' Utilizzando la risposta Crawler che facciamo il controllo per verificare il messaggio flash se è presente nella risposta restituita. Eseguire il test per verificare che tutto funzioni correttamente.

 

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

Il test fallisce . Ci viene dato il seguente output da PHPUnit.

1) Blogger\BlogBundle\Tests\Controller\PageControllerTest::testContact
Failed asserting that <integer:0> matches expected <integer:1>.

/var/www/html/symblog/symblog/src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php:53

FAILURES!
Tests: 3, Assertions: 5, Failures: 1.

L'uscita ci informa che il messaggio flash non è stato trovato nella risposta dal modulo di invio. Questo perché quando siamo nell'ambiente test ,i redirect non vengono eseguiti. Quando il form viene convalidato correttamente nella classe PageController si ha un redirect . Questo reindirizzamento non viene eseguito; Abbiamo bisogno di dire esplicitamente che il redirect dovrebbe essere seguita. La ragione per cui i redirect non vengono rispettati è semplice, si consiglia di verificare prima la risposta corrente. Dimostreremo così che la posta elettronica era stata inviata. Aggiornare la classe PageControllerTest per impostare il client per eseguire il redirect.

// src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

public function testContact()
{
    // ..

    $crawler = $client->submit($form);

    // Need to follow redirect
    $crawler = $client->followRedirect();

    $this->assertEquals(1, $crawler->filter('.blogger-notice:contains("Your contact enquiry was successfully sent. Thank you!")')->count());
}

Quando si eseguono i test PHPUnit questi dovrebbero essere superati. Ora guardiamo alla fase finale di verifica del processo di contatto modulo di invio, punto 4, controlliamo se una e-mail è stata inviata a symblog. Sappiamo già che le email non saranno consegnate nell'ambiente di test a causa della seguente configurazione.

# app/config/config_test.yml

swiftmailer:
    disable_delivery: true

Siamo in grado di testare se le email sono state inviate utilizzando le informazioni raccolte dal profiler web. La verifica del profiler deve essere fatto prima che il reindirizzamento avvenga, in quanto le informazioni contenute nel profiler andranno perse. Aggiornare il metodo testContact () con il seguente .

// src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

public function testContact()
{
    // ..

    $crawler = $client->submit($form);

    // Check email has been sent
    if ($profile = $client->getProfile())
    {
        $swiftMailerProfiler = $profile->getCollector('swiftmailer');

        // Only 1 message should have been sent
        $this->assertEquals(1, $swiftMailerProfiler->getMessageCount());

        // Get the first message
        $messages = $swiftMailerProfiler->getMessages();
        $message  = array_shift($messages);

        $symblogEmail = $client->getContainer()->getParameter('blogger_blog.emails.contact_email');
        // Check message is being sent to correct address
        $this->assertArrayHasKey($symblogEmail, $message->getTo());
    }

    // Need to follow redirect
    $crawler = $client->followRedirect();

    $this->assertTrue($crawler->filter('.blogger-notice:contains("Your contact enquiry was successfully sent. Thank you!")')->count() > 0);
}

Dopo l'invio del form controlliamo per vedere se il profiler è disponibile siccome potrebbe essere stato disabilitato da un'impostazione di configurazione per l'ambiente corrente.

Nota

Ricordate i test non devono essere eseguiti nell'ambiente di test , possono essere eseguiti nell'ambiente prod in cui cose come il profiler non saranno disponibili.

Se siamo in grado di ottenere il profiler facciamo una richiesta per recuperare il SwiftMailer collettore. Il SwiftMailer collettore lavora dietro le quinte per raccogliere informazioni su come il servizio email viene utilizzato. Possiamo usare questo per ottenere informazioni in merito, di come le email sono state inviate.

Poi si usa il metodo getMessageCount () per controllare che 1 mail è stata inviata. Questo è sufficiente a garantire che almeno una e-mail sia inviata, ma non verifica che l'email verrà inviata alla posizione corretta. Potrebbe essere molto imbarazzante o addirittura dannoso inviare a un indirizzo email errato.

Ora nuovamente eseguire i test per verificare che tutto funzioni correttamente.

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php

Test sull'Aggiunta di commenti nel blog

Utilizziamo le conoscenze che abbiamo acquisito dai test precedenti per la pagina dei contatti per testare il processoi per presentare un commento nel blog. Ancora una volta dobbiamo delineare quello che dovrebbe accadere quando il form viene inviato correttamente.

  1. Passare a una pagina di blog
  2. Popolare il form di commento con i valori
  3. Invio del form
  4. Controllare che il nuovo commento venga aggiunto alla fine della lista commento nel blog
  5. Verificate anche i commenti più recenti della barra laterale per garantire che il commento sia in cima alla lista

Creare un nuovo file in src / Blogger / BlogBundle / Test / controller / BlogControllerTest.php e aggiungere iil seguente..

<?php
// src/Blogger/BlogBundle/Tests/Controller/BlogControllerTest.php

namespace Blogger\BlogBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class BlogControllerTest extends WebTestCase
{
    public function testAddBlogComment()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', '/1/a-day-with-symfony');

        $this->assertEquals(1, $crawler->filter('h2:contains("A day with Symfony2")')->count());

        // Select based on button value, or id or name for buttons
        $form = $crawler->selectButton('Submit')->form();

        $crawler = $client->submit($form, array(
            'blogger_blogbundle_commenttype[user]'          => 'name',
            'blogger_blogbundle_commenttype[comment]'       => 'comment',
        ));

        // Need to follow redirect
        $crawler = $client->followRedirect();

        // Check comment is now displaying on page, as the last entry. This ensure comments
        // are posted in order of oldest to newest
        $articleCrawler = $crawler->filter('section .previous-comments article')->last();

        $this->assertEquals('name', $articleCrawler->filter('header span.highlight')->text());
        $this->assertEquals('comment', $articleCrawler->filter('p')->last()->text());

        // Check the sidebar to ensure latest comments are display and there is 10 of them

        $this->assertEquals(10, $crawler->filter('aside.sidebar section')->last()
                                        ->filter('article')->count()
        );

        $this->assertEquals('name', $crawler->filter('aside.sidebar section')->last()
                                            ->filter('article')->first()
                                            ->filter('header span.highlight')->text()
        );
    }
}

Questa volta dovrebbe passare l'intero test . Prima di iniziare la dissezione del codice, eseguire i test per questo file per assicurarsi che tutto funzioni correttamente.

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/BlogControllerTest.php

PHPUnit ci dovrebbe informare che il 1 test è stato eseguito con successo. Guardando il codice per il testAddBlogComment () possiamo vedere le cose iniziano nel formato usuale, la creazione di un client, richiesta di una pagina e controllo se la pagina è corretta. Si prosegue poi per ottenere il form add commento, e inviare il form. Il nostro modo di popolare i valori del form è leggermente diverso rispetto alla versione precedente. Questa volta si usa il secondo argomento del client per il metodo submit() per passare i valori del form.

Nota

Potremmo anche utilizzare l'interfaccia Object Oriented per impostare i valori dei campi del form. Alcuni esempi sono mostrati sotto.

// Tick a checkbox
$form['show_emal']->tick();

// Select an option or a radio
$form['gender']->select('Male');

Dopo aver inviato il form, chiediamo al client di eseguire il redirect in modo da poter verificare la risposta. Usiamo il Crawler di nuovo per ottenere l'ultimo commento blog, che dovrebbe essere quella che abbiamo appena presentato. Infine controlliamo gli ultimi commenti nella sidebar per controllare se il commento è anche il primo nella lista.

Blog Repository

Nell''ultima parte dei test funzionali che esploreremo in questo capitolo andiamo a testare i 2 repository Doctrine. Creare un nuovo file in src / Blogger / BlogBundle / Test / repository / BlogRepositoryTest.php e aggiungere il seguente cont

<?php
// src/Blogger/BlogBundle/Tests/Repository/BlogRepositoryTest.php

namespace Blogger\BlogBundle\Tests\Repository;

use Blogger\BlogBundle\Repository\BlogRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class BlogRepositoryTest extends WebTestCase
{
    /**
     * @var \Blogger\BlogBundle\Repository\BlogRepository
     */
    private $blogRepository;

    public function setUp()
    {
        $kernel = static::createKernel();
        $kernel->boot();
        $this->blogRepository = $kernel->getContainer()
                                       ->get('doctrine.orm.entity_manager')
                                       ->getRepository('BloggerBlogBundle:Blog');
    }

    public function testGetTags()
    {
        $tags = $this->blogRepository->getTags();

        $this->assertTrue(count($tags) > 1);
        $this->assertContains('symblog', $tags);
    }

    public function testGetTagWeights()
    {
        $tagsWeight = $this->blogRepository->getTagWeights(
            array('php', 'code', 'code', 'symblog', 'blog')
        );

        $this->assertTrue(count($tagsWeight) > 1);

        // Test case where count is over max weight of 5
        $tagsWeight = $this->blogRepository->getTagWeights(
            array_fill(0, 10, 'php')
        );

        $this->assertTrue(count($tagsWeight) >= 1);

        // Test case with multiple counts over max weight of 5
        $tagsWeight = $this->blogRepository->getTagWeights(
            array_merge(array_fill(0, 10, 'php'), array_fill(0, 2, 'html'), array_fill(0, 6, 'js'))
        );

        $this->assertEquals(5, $tagsWeight['php']);
        $this->assertEquals(3, $tagsWeight['js']);
        $this->assertEquals(1, $tagsWeight['html']);

        // Test empty case
        $tagsWeight = $this->blogRepository->getTagWeights(array());

        $this->assertEmpty($tagsWeight);
    }
}

Sicome si desidera eseguire i test che richiedono una connessione valida al database si estende la WebTestCase questo ci permette di bootstrap del kernel Symfony2. Esegui il test per questo file usando il seguente comando.

$ phpunit -c app/ src/Blogger/BlogBundle/Tests/Repository/BlogRepositoryTest.php

Code Coverage

Prima di proseguire rapidamente permettetemi di toccare il code coverage. Coverage code ci dà una visione delle parti del codice,che vengono eseguite quando appunto vengono eseguiti i test . Usando questo possiamo vedere le parti del nostro codice che non hanno test eseguiti , e determinare se abbiamo bisogno di scrivere test per queste .

Per emettere l'analisi di coverange code per l'applicazione eseguire il seguente

$ phpunit --coverage-html ./phpunit-report -c app/

Questo produrrà l'analisi code coverage nella cartella PHPUnit-report . Aprire il file index.html nel browser per visualizzare l'output di analisi.

Vedere il capitolo Code Coverage Analysis nella documentazione PHPUnit per ulteriori informazioni.

Conclusione

Abbiamo coperto un certo numero di settori chiave per quanto riguarda i test. Abbiamo esplorato sia test unit e test funzionali per assicurare che il nostro sito web funziona correttamente. Abbiamo visto come simulare richieste del browser e come utilizzare la classe Symfony2 Crawler per verificare le risposte a queste richieste.

Ora vediamo il componente di sicurezza Symfony2, e più specificamente come usarlo per la gestione degli utenti. Si andrà anche ad integrare il FOSUserBundle pronto per noi per lavorare sulla sezione admin symblog.

We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.
blog comments powered by Disqus

«  [Part 5] - Customising the view: Twig extensions, The sidebar and Assetic   ::   Contents

Free Web Hosting