DY

05 Test unitaire & d'intégration

Vous pouvez reprendre le contenu de votre TP2 pour ce TP, peut être même un fork de votre dépôt ? 😉

Et les tests dans tout ça ?

center

Déjà un TD sur les tests unitaires et d'intégrations ? Si tôt dans la découverte du module ?

Et oui ! C'est bien beau de parler de TDD mais encore faut il le mettre en place et savoir en faire.

Plutôt que de tout tester à la main de maniére répétitif et fastidieuse, nous allons apprendre à les automatiser pour notre plus grand bonheur 😀.

Pré-requis

Pour réaliser les tests unitaires et d'intégrations nous aurons besoin des librairies php suivantes:

Un petit composer require pour l'installation ?

Structure de notre dossier

En reprenant la structure de votre TP2 vous devriez avoir:

-- src/
-- vendor/
-- index.php
-- composer.json
-- composer.lock

Nous allons donc créer un répertoire tests à la racine de notre projet qui contiendra nos fichier de tests.

Premier test unitaire

L'exemple le plus simple pour comprendre le principe des tests unitaires est la fonction de multiplication.

Pour rappel une multiplication est l'opération mathématique permettant d'obtenir le produit de deux facteur (nommés facteur gauche et facteur droite).

Mise en place
  1. Créer un fichier (vide) functions.php dans le répertoire src/
  2. Créer dans le répertoire tests/ un fichier UnitariesTest.php
  3. Pensez à modifier votre composer.json pour charger automatiquement le fichier de fonctions qui se trouve dans le répertoire src/
PHPUnit

PHPUnit est un framework open source de tests unitaires dédié au langage de programmation PHP.

Il permet l'implémentation des tests de régression en vérifiant que les exécutions correspondent aux assertions prédéfinies.

Premier test

Modifier le fichier UnitariesTest.php pour que son contenu soit:

<?php

require_once 'vendor/autoload.php';

use PHPUnit\Framework\TestCase;

class UnitariesTest extends TestCase {

    public function test_multiply(){
        $this->assertEquals(4, multiply(2, 2));
    }
}

Nous venons de définir une classe de tests qui posséde un fonction permettant de tester notre fonction de multiplication (notez la présence du require pour charger l'autoloader de composer).

La ligne 9 permet de vérifier que la fonction retourne bien le produit de 2 par 2 (à savoir 4).

Nous avons définit notre premier test, il serait donc temps de le lancer via la commmande suivante:

$ vendor/bin/phpunit tests/UnitariesTest.php

Normalement vous devriez avoir l'erreur suivante :

PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

E                                                                   1 / 1 (100%)

Time: 15 ms, Memory: 4.00MB

There was 1 error:

1) MultiplyTest::test_multiply
Error: Call to undefined function multiply()

/home/sam/php/tests/UnitariesTest.php:8

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

C'est normal ! Nous respectons la philosophie TDD à savoir écrire nos tests d'abord puis vérifier que ceux-ci échouent !

Il convient de créer maintenant notre fonction de multiplication, copier-coller dans votre fichier functions.php le code (sans erreur) suivant:

<?php

function multiply($facteur_gauche, $facteur_droite)
{
    return $facteur_gauche + $facteur_droite;
}

Relancez votre test via la commmande suivante:

$ vendor/bin/phpunit tests/UnitariesTest.php

Notre test passe ! Mais notre jeu de données n'est pas complet ! Il convient donc d'ajouter une assertion supplémentaire à notre test.

  1. Ajoutez l'assertion que le produit de 3 par 7 est 21
  2. Relancez votre test que constatez vous ?
  3. Corrigez le code de votre fonction pour régler ce souci.

Bravo vous venez de faire votre première suite de test unitaire 🎉.

Remarque

Le répertoire tests ainsi que le fait de suffixer nos fichier par Test.php est une convention qui permet à php unit de charger de manière automatique l'ensemble des tests du répertoire.

Il suffit de lancer la commande suivante pour lancer l'ensemble des tests du répertoire tests/:

$ vendor/bin/phpunit tests/

Test d'intégration

Tester nos fonctions c'est bien, utile et indispensable. Toutefois il est tout aussi indispensable de tester le retour de nos pages, nous allons donc voir comment mettre en place des tests d'intégrations.

Création d'un client de test

Pour pouvoir tester notre site en intégration il convient de créer un client de tests. Votre professeur étant fort sympatique en voici un à placer dans le fichier src/utils.php

<?php

use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\Process;
use GuzzleHttp\Client;

abstract class IntegrationTestCase extends TestCase {

    private static $process;

    public static function setUpBeforeClass(): void
    {
        self::$process = new Process(["php", "-S", "localhost:8080", "-t", "."]);
        self::$process->start();
        usleep(100000); //wait for server to get going
    }

    public static function tearDownAfterClass(): void
    {
        self::$process->stop();
    }

    public function get_client()
    {
        return new Client(['http_errors' => false]);
    }

    public function build_url($url)
    {
        return "localhost:8080" . $url;
    }

    public function make_request($method, $url)
    {
        $client =  $this->get_client();
        return $client->request($method, $this->build_url($url));
    }
}

Ce client lance en processus votre site web et permet de tester le retour de requête HTTP sur celui-ci.

Notre premier test d'intégration

Créer un fichier IntegrationsTest.php dans le répertoire tests/ avec le contenu suivant:

<?php
require_once 'vendor/autoload.php';

class PagesIntegrationTest extends IntegrationTestCase{

    public function test_index()
    {
        $response = $this->make_request("GET", "/");
        $this->assertEquals(200, $response->getStatusCode());
        $this->assertEquals("Hello World!", $response->getBody()->getContents());
        $this->assertContains("text/html", $response->getHeader('Content-Type')[0]);
    }
}

Le test d'intégration test_index permet de:

Vous savez désormais tester en intégration une page de votre site web.

Un test un peu plus complexe
  1. Comment mettre en place un test d'intégration pour la route hello de votre site ? Pensez à l'esprit TDD et comment tester cela.
  2. Et si notre route hello retourner une string sous la forme <h2>Hello $name </h2> ?
  3. Et si on utiliser l'assertion assertContains ?