Advertisement
  1. Code
  2. TDD

Test-Driven Development con Laravel e Doctrine

Scroll to top
Read Time: 8 min

() translation by (you can also view the original English article)

Da sviluppatore PHP, puoi usare la tecnica del Test-Driven Development (TDD) per sviluppare il tuo software scrivendo test. Solitamente, il TDD divide ciascuna attività dello sviluppo in unità individuali. Un test è quindi scritto per assicurare che l'unità si comporti come previsto.

Ogni progetto che utilizza il Test-Driven Development segue ripetutamente tre semplici step:

  • scrivi un test per il prossimo pezzo di funzionalità che vuoi aggiungere.
  • scrivi il codice funzionale fino a quando il test lo supera.
  • fai il refactoring sia del nuovo che del vecchio codice per renderlo ben strutturato.

Continua a ciclare questi tre step, un test alla volta, realizzando la funzionalità del sistema. Testare ti aiuterà nel refactoring, azione che consente di migliorare il progetto col tempo e rende alcuni problemi di progettazione più evidenti.

I test che contengono piccoli componenti individuali sono chiamati unit test (test unitari). Sebbene gli unit test possano essere portati a termine indipendentemente, se testi alcuni componenti quando sono integrati in altri, stai facendo integration testing (test di integrazione). Il terzo tipo di testing è il test stubs. Il test stubs ti permette di testare il codice senza avere chiamate reali al database.

Perché TDD Funziona

Al giorno d'oggi, visto che puoi usare la sintassi dei moderni IDE PHP, il feedback non è un grosso problema. Uno degli aspetti importanti dello sviluppo è essere certi che il codice si comporti come previsto. Dato che il software è complesso (differenti componenti integrati gli uni agli altri), è facile che molte delle nostre aspettative vengano disattese. Specialmente alla fine del progetto, a causa dello sviluppo, il progetto diventa più complicato e quindi più difficile da debuggare e testare.

Il TDD verifica che il codice faccia quello che ci si aspetta. Se qualcosa va male, ci sono solo poche linee di codice da ricontrollare. Gli errori sono facili da trovare e da correggere. Nel TDD, il test si focalizza sul comportamento, non sull'implementazione. Il TDD produce codice che è stato progettato, testato e collaudato.

PHPUnit e Laravel

PHPUnit è lo standard di fatto per lo unit testing in PHP. È essenzialmente un framework per scrivere test e fornisce gli strumenti di cui hai bisogno per eseguire i test e analizzare i risultati. PHPUnit trae la sua struttura e funzionalità dall' SUnit di Kent Beck.

Esistono molte differenti asserzioni che possono aiutarti a testare i risultati di tutte le possibili chiamate nelle applicazioni. A volte occorre essere un po' più creativi per testare parti funzionali più complesse, ma le asserzioni fornite da PHPUnit coprono la maggior parte delle situazioni da testare. Ecco una lista delle più comuni che incontrerai e che userai nei test.

  • AssertTrue: controlla l'input per verificare che sia uguale a true.
  • AssertFalse: controlla l'input per verificare che sia uguale a false.
  • AssertEquals: controlla il risultato con un input per verificare se sono uguali.
  • AssertArrayHasKey: restituisce un errore se nell'array non c'è la chiave ricercata.
  • AssertGreaterThan: controlla il risultato per vedere se è più grande di un valore.
  • AssertContains: controlla che l'input contenga un determinato valore.
  • AssertType: controlla che una variabile sia di un certo tipo.
  • AssertNull: controlla che una variabile sia null.
  • AssertFileExists: verifica che un file esista.
  • AssertRegExp: controlla che l'input soddisfi un'espressione regolare.

Di default, PHPUnit 4.0 è già installato in Laravel e potrebbe essere necessario lanciare il seguente comando per aggiornarlo:

1
composer global require "phpunit/phpunit=5.0.*"

Il file phpunit.xml, nella directory root di Laravel, ti consente di effettuare alcune configurazioni. In questo caso, se vuoi sovrascrivere la configurazione di default, puoi editare il file.

1
<?xml version="1.0" encoding="UTF-8"?>
2
<phpunit backupGlobals="false"
3
         backupStaticAttributes="false"
4
         bootstrap="bootstrap/autoload.php"
5
         colors="true"
6
         convertErrorsToExceptions="true"
7
         convertNoticesToExceptions="true"
8
         convertWarningsToExceptions="true"
9
         processIsolation="false"
10
         stopOnFailure="false"
11
         syntaxCheck="false">
12
    <testsuites>
13
        <testsuite name="Application Test Suite">
14
            <directory>./tests/</directory>
15
        </testsuite>
16
    </testsuites>
17
    <filter>
18
        <whitelist>
19
            <directory suffix=".php">app/</directory>
20
        </whitelist>
21
    </filter>
22
    <php>
23
        <env name="APP_ENV" value="testing"/>
24
        <env name="CACHE_DRIVER" value="array"/>
25
        <env name="SESSION_DRIVER" value="array"/>
26
        <env name="QUEUE_DRIVER" value="sync"/>
27
        <env name="DB_DEFAULT" value="sqlite_testing"/>
28
        <env name="DB_HOST" value="localhost"/>
29
        <env name="DB_DATABASE" value="laravel"/>
30
        <env name="DB_USERNAME" value="root"/>
31
        <env name="DB_PASSWORD" value="root"/>
32
    </php>
33
</phpunit>
34

Come puoi vedere dal codice sopra, ho aggiunto la configurazione del database di prova (non usato nell'articolo).

Cos'è l'ORM Doctrine?

Doctrine è un ORM che implementa il pattern data mapper e consente di creare una netta separazione tra la logica dell'applicazione e il persistence layer del database. Per usare Doctrine, esiste un'implementazione da utilizzare con una configurazione esistente di Laravel 5. Per installare Doctrine 2 nel tuo progetto Laravel, esegui il seguente comando:

1
composer require laravel-doctrine/orm

Come di consueto, il package dovrebbe essere aggiunto alla lista dei service provider in app/config.php.

1
LaravelDoctrine\ORM\DoctrineServiceProvider::class,

Devi configurare anche l'alias:

1
'EntityManager' => LaravelDoctrine\ORM\Facades\EntityManager::class

Infine, pubblichiamo la configurazione del package con:

1
php artisan vendor:publish --tag="config"

Come Testare i Repository di Doctrine

Prima di qualsiasi altra cosa, dovresti approfondire le fixture. Le fixture sono usate per caricare in un database un limitato set di dati da testare. Fortunatamente, Doctrine 2 fornisce una libreria per aiutarti a scrivere le fixture per l'ORM Doctrine.

Per installare il bundle delle fixture nella tua App Laravel, abbiamo bisogno di eseguire il seguente comando:

1
composer require --dev doctrine/doctrine-fixtures-bundle

Creiamo la nostra fixture in tests/Fixtures.php:

1
namespace Test;
2
use Doctrine\Common\Persistence\ObjectManager;
3
use Doctrine\Common\DataFixtures\FixtureInterface;
4
use app\Entity\Post;
5
6
class Fixtures implements FixtureInterface
7
{
8
    /**
9
     * Load the Post fixtures
10
     * @param ObjectManager $manager
11
     * @return void
12
     */
13
    public function load(ObjectManager $manager)
14
    {
15
        $Post = new Post(['title'=>'hello world','body'=>'this is body']);
16
        $manager->persist($Post);
17
        $manager->flush();
18
    }
19
}
20

Come puoi osservare, la classe fixture deve implementare la FixtureInterface e deve avere il metodo load(ObjectManager $manager). Le fixture di Doctrine 2 sono classi PHP in cui puoi creare oggetti e renderli persistenti nel database. Per fare l'autoload delle nostre fixture, in Laravel, dobbiamo modificare il file composer.json nella root di Laravel.

1
...
2
    "autoload-dev": {
3
        "classmap": [
4
            "tests/TestCase.php",
5
            "tests/Fixtures.php" //added here
6
        ]
7
    },
8
...

Esegui quindi:

1
composer dump-autoload

Creiamo il nostro file di test DoctrineTest.php nella directory tests.

1
namespace Test;
2
use App;
3
use App\Entity\Post;
4
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
5
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
6
use Doctrine\Common\DataFixtures\Loader;
7
use App\Repository\PostRepo;
8
9
class doctrineTest  extends TestCase
10
{
11
    private $em;
12
    private $repository;
13
    private $loader;
14
    public function setUp()
15
    {
16
        parent::setUp();
17
        $this->em = App::make('Doctrine\ORM\EntityManagerInterface');
18
        $this->repository = new PostRepo($this->em);
19
        $this->executor = new ORMExecutor($this->em, new ORMPurger);
20
        $this->loader = new Loader;
21
        $this->loader->addFixture(new Fixtures);
22
    }
23
24
    /** @test */
25
    public function post()
26
    {
27
        $purger = new ORMPurger();
28
        $executor = new ORMExecutor($this->em, $purger);
29
        $executor->execute($this->loader->getFixtures());
30
        $user = $this->repository->PostOfTitle('hello world');
31
        $this->em->clear();
32
        $this->assertInstanceOf('App\Entity\Post', $user);
33
    }
34
}

Nel metodo setUp(), ho instanziato l'ORMExecutor e il Loader. Carichiamo anche la classe Fixtures che abbiamo appena implementato.

Non dimenticare che l'annotazione /** @test */ è molto importante, e senza di essa phpunit restituirebbe l'errore No tests found in class.

Per iniziare il test nella root del progetto, esegui semplicemente il comando:

1
sudo  phpunit 

Il risultato sarà:

1
PHPUnit 4.6.6 by Sebastian Bergmann and contributors.
2
3
Configuration read from /var/www/html/laravel/phpunit.xml.
4
Time: 17.06 seconds, Memory: 16.00M
5
OK (1 test, 1 assertion)

Se devi condividere oggetti tra le fixture, puoi farlo aggiungendo semplicemente una referenza a quell'oggetto dandogli un nome e richiamandolo successivamente per formare una relazione. Ecco un esempio:

1
namespace Test;
2
use Doctrine\Common\Persistence\ObjectManager;
3
use Doctrine\Common\DataFixtures\FixtureInterface;
4
use app\Entity\Post;
5
6
class PostFixtures implements FixtureInterface
7
{
8
    /**
9
     * Load the User fixtures
10
     *
11
     * @param ObjectManager $manager
12
     * @return void
13
     */
14
    public function load(ObjectManager $manager)
15
    {
16
        $postOne = new Post(['title'=>'hello','body'=>'this is body']);
17
        $postTwo = new Post(['title'=>'hello there ','body'=>'this is body two']);
18
        $manager->persist($postOne);
19
        $manager->persist($postTwo);
20
        $manager->flush();
21
22
        // store reference to admin role for User relation to Role
23
        $this->addReference('new-post',$postOne);
24
    }
25
}

e la fixture Comment:

1
namespace Test;
2
use Doctrine\Common\Persistence\ObjectManager;
3
use Doctrine\Common\DataFixtures\FixtureInterface;
4
use app\Entity\Post;
5
6
class CommentFixtures implements FixtureInterface
7
{
8
    /**
9
     * Load the User fixtures
10
     *
11
     * @param ObjectManager $manager
12
     * @return void
13
     */
14
    public function load(ObjectManager $manager)
15
    {
16
        $comment = new Comment(['title'=>'hello','email'=>'alirezarahmani@live.com','text'=>'nice post']);
17
        $comment->setPost($this->getReference('new-post')); // load the stored reference
18
        $manager->persist($comment);
19
        $manager->flush();
20
        // store reference to new post for Comment relation to post
21
        $this->addReference('new-post',$postOne);
22
    }
23
}

Con i due metodi getReference()setReference(), puoi condividere uno o più oggetti tra le fixture.

Se per te è importante ordinare le fixture, puoi farlo semplicemente con il metodo getOrder nelle tue fixture, come mostrato qui di seguito:

1
    public function getOrder()
2
    {
3
        return 5; // number in which order to load fixtures
4
    }

Presta attenzione al fatto che l'ordinamento è rilevante per la classe Loader.

Una delle cose più importanti delle fixture è la loro capacità di risolvere problemi di dipendenze. L'unica cosa che devi aggiungere è un metodo nella tua fixture, come ho fatto qui sotto:

1
    public function getDependencies()
2
    {
3
        return array('Test\CommentFixtures'); // fixture classes fixture is dependent on
4
    }

Conclusioni

Questa è solo un accenno al Test-Driven Development con Laravel 5 e PHPUnit. Nel test dei repository, è inevitabile che si utilizzi il database. In questo caso, le fixture di Doctrine sono importanti.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.