Introduction au test logiciel/Tests unitaires/PHPUnit

Un livre de Wikilivres.
Sauter à la navigation Sauter à la recherche

PHPUnit est utilisé dans un certain nombre de frameworks connus pour réaliser des tests unitaires. Sa documentation en anglais est disponible au format PDF[1].

Installation[modifier | modifier le wikicode]

Via composer[modifier | modifier le wikicode]

 composer require --dev phpunit/phpunit ^8

Via wget[modifier | modifier le wikicode]

Une fois le .phar téléchargé depuis le site officiel[2], le copier dans le dossier où il sera toujours exécuté. Exemple :

Unix-like[modifier | modifier le wikicode]

wget https://phar.phpunit.de/phpunit-8.phar
mv phpunit.phar /usr/local/bin/phpunit
chmod +x phpunit.phar

Windows[modifier | modifier le wikicode]

  1. Ajouter à la variable d'environnement PATH, le dossier où se trouve le fichier (ex : ;C:\bin).
  2. Créer un fichier exécutable à côté (ex : C:\bin\phpunit.cmd) contenant le code : @php "%~dp0phpunit.phar" %*.

Par ailleurs, le code source de cet exécutable est sur GitHub[3].

Test[modifier | modifier le wikicode]

Test de l'installation :

phpunit --version

Utilisation[modifier | modifier le wikicode]

Il faut indiquer au programme les dossiers contenant des tests dans le fichier phpunit.xml.dist. Exemple sur Symfony[4] :

<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.0/phpunit.xsd"
         backupGlobals="false"
         colors="true"
         bootstrap="vendor/autoload.php"
>
    <php>
        <ini name="error_reporting" value="-1" />
        <server name="KERNEL_CLASS" value="AppKernel" />
        <env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
    </php>

    <testsuites>
        <testsuite name="Project Test Suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist>
            <directory>src</directory>
            <exclude>
                <directory>src/*</directory>
            </exclude>
        </whitelist>
    </filter>

    <listeners>
        <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
    </listeners>

</phpunit>

Ensuite, pour tester tous les fichiers du dossier /test et ignorer ceux de /src, il suffit de lancer :

./bin/phpunit

Si les tests sont longs et qu'on ne travaille que sur une seule classe ou une seule méthode, on peut demander à ne tester qu'elle en précisant son nom (ce qui évite d'afficher des dumps que l'on ne souhaite pas voir lors des autres tests) :

./bin/phpunit --filter=MaClasseTest
./bin/phpunit --filter=MaMethodeTest

Écriture des tests[modifier | modifier le wikicode]

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class SuiteDeTests1 extends TestCase
{
    /** @var MockObject */
    private $monMock1;

    protected function setUp(): void
    {
        // Création des mocks et instanciation de la classe à tester...
        $this->monMock1 = $this->$this->getMockBuilder(maMockInterface::class)->getMock();
    }

    protected function tearDown(): void
    {
        // Libération des ressources après les tests...
    }

    protected function test1()
    {
        // Lancement du premier test...
        $this->assertTrue($condition);
    }
}

La classe de test PHPUnit propose des dizaines d'assertions différentes.

MockObject[modifier | modifier le wikicode]

Les mocks permettent de simuler des résultats de classes existantes[5].

willReturn()[modifier | modifier le wikicode]

Par exemple, pour simuler le résultat de deux classes imbriquées (en appelant la méthode d'une méthode), on leur crée une méthode de test chacune :

    public function mainTest()
    {
        $this->monMock1
            ->expects($this->once())
            ->method('MaMéthode1')
            ->willReturn($this->mockProvider())
        ;

        $this->assertEquals(null, $this->monMock1->MaMéthode1()->MaMéthode2());
    }

    private function mockProvider()
    {
        $monMock = $this
            ->getMockBuilder('MaClasse1')
            ->getMock()
        ;
        $monMock->method('MaMéthode2')
            ->willReturn('MonRésultat1')
        ;

        return $monMock;
    }

Logo Pour qu'une méthode de mock réalise un "set" quand elle est appelée, il ne faut pas le faire directement dans le willReturn, auquel cas il s'effectue lors de sa définition, mais dans un callback. Ex :

        $monMock->method('MaMéthode3')
            ->will($this->returnCallback(function($item) use ($quantity) {
                return $item->setQuantity($quantity);
            }))
        ;


expects()[modifier | modifier le wikicode]

Dans l'exemple précédent, expects() est un espion qui compte le nombre de passage dans la méthode, et le test échoue si ce résultat n'est pas 1. Ses valeurs possibles sont :

  • $this->never() : 0.
  • $this->once() : 1.
  • $this->exactly(x) : x.
  • $this->any().

onConsecutiveCalls[modifier | modifier le wikicode]

Si la valeur retournée par le mock doit changer à chaque appel, il faut remplacer willReturn() par onConsecutiveCalls().

Exemple :

    $this->enumProvider->method('getEnumFromVariable')
        ->will($this->onConsecutiveCalls(
            ProductStatusEnum::ON_LINE,
            OrderStatusEnum::VALIDATED
        ));
    ;

with()[modifier | modifier le wikicode]

Cette méthode permet de définir les paramètres avec lesquels doit être lancé une méthode mock. Ex :

    $this->enumProvider->method('getEnumFromVariable')
        ->with($this->equalTo('variable 1'))

disableOriginalConstructor()[modifier | modifier le wikicode]

Cette méthode s'emploie quand il est inutile de passer par le constructeur du mock.

setExpectedException()[modifier | modifier le wikicode]

S'utilise Quand le test unitaire doit provoquer une exception.

Annotations[modifier | modifier le wikicode]

PHPUnit offre plusieurs annotations pour influencer les tests[6]. Exemples :

  • @covers : renseigne la méthode testée par une méthode de test afin de calculer le taux de couverture du programme par les tests.
  • @uses : indique les classes instanciées par le test.
  • @dataProvider : indique un tableau d'entrées et de sorties attendues lors d'un test[7].

JavaScript[modifier | modifier le wikicode]

En PHP, Selenium peut s'interfacer avec PHPUnit[8] pour tester du JavaScript.

Références[modifier | modifier le wikicode]