• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 12/05/2022

Approfondissez vos tests

Dans le chapitre précédent, nous avons vu comment :

  • écrire le test permettant de s'assurer qu'un objet  $user  de type  App\Entity\User  est bien retourné ;

  • créer une classe de test  GithubUserProviderTest  dans le dossier  tests/Security  .

Maintenant, nous allons aller plus loin en :

  1. Écrivant le test qui couvre le cas où aucune information ne serait récupérée après la requête HTTP à GitHub.

  2. Factorisant le code.

Écrivez le test couvrant le cas où aucune information ne serait récupérée après la requête HTTP à GitHub

Voici un cas qu’il nous reste à couvrir dans la méthode  loadUserByUsername  .

Vous avez toutes les clés pour y arriver ! Essayez  seul avant de regarder la correction.

Alors ?

Allez, on vous donne la correction :

<?php
namespace App\Tests\Security;
// …
use App\Security\GithubUserProvider;
class GithubUserProviderTest extends TestCase
{
    // …
    public function testLoadUserByUsernameThrowingException()
    {
        $client = $this->getMockBuilder('GuzzleHttp\Client')
            ->disableOriginalConstructor()
            ->setMethods(['get'])
            ->getMock();
        $serializer = $this
            ->getMockBuilder('JMS\Serializer\Serializer')
            ->disableOriginalConstructor()
            ->getMock();
        $response = $this
        
->getMockBuilder('Psr\Http\Message\ResponseInterface')
            ->getMock();
        $client
             ->expects($this->once()) // Nous nous attendons à ce que la méthode get soit appelée une fois
            ->method('get')
            ->willReturn($response);
        $streamedResponse = $this
            ->getMockBuilder('Psr\Http\Message\StreamInterface')
            ->getMock();
        $response
             ->expects($this->once()) // Nous nous attendons à ce que la méthode getBody soit appelée une fois
            ->method('getBody')
            ->willReturn($streamedResponse);
        $streamedResponse
             ->expects($this->once())
            ->method('getContents')
            ->willReturn('foo');
        $serializer
             ->expects($this->once()) // Nous nous attendons à ce que la méthode deserialize soit appelée une fois
            ->method('deserialize')
            ->willReturn([]);
 
        $this->expectException('LogicException');
           $githubUserProvider = new GithubUserProvider($client, $serializer);
$githubUserProvider->loadUserByUsername('an-access-token');
    }

}

Il suffit de faire en sorte que le double (stub) du  serializer  retourne un tableau vide, et le tour est joué !

Allez, on arrive au bout, il ne vous reste plus qu’à factoriser le code.

Factorisez le code

Vous avez remarqué que bon nombre des doublures sont utilisées dans les deux tests unitaires. Il est possible de les initialiser à chaque début de test grâce à la méthode  setUp  .

Voyons tout cela en vidéo :

La méthode setUp

La méthode setUp est une méthode provenant de la classe  PHPUnit_Framework_TestCase  , que l'on peut surcharger pour exécuter des instructions avant chaque test de la classe.

Vous allez donc faire en sorte que toutes les doublures utilisées dans les tests unitaires soient initialisées à chaque début de test (méthode de test).

Voici le code remis au propre après cette étape de factorisation :

<?php
namespace App\Tests\Security;

use App\Entity\User;
use App\Security\GithubUserProvider;
use GuzzleHttp\Client;
use JMS\Serializer\Serializer;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

class GithubUserProviderTest extends TestCase
{
    private MockObject | Client | null $client;
    private MockObject | Serializer | null $serializer;
            private MockObject | StreamInterface | null $streamedResponse;
    private MockObject | ResponseInterface | null $response;

    public function setUp(): void
    {
                                $this->client = $this->getMockBuilder('GuzzleHttp\Client')
            ->disableOriginalConstructor()
            ->setMethods(['get'])
            ->getMock();
        $this->serializer = $this
            ->getMockBuilder('JMS\Serializer\Serializer')
            ->disableOriginalConstructor()
            ->getMock();
        $this->streamedResponse = $this
            ->getMockBuilder('Psr\Http\Message\StreamInterface')
            ->getMock();
        $this->response = $this
        
->getMockBuilder('Psr\Http\Message\ResponseInterface')
            ->getMock();
    }

    public function testLoadUserByUsernameReturningAUser()
    {
        $this->client
            ->expects($this->once())
            ->method('get')
            ->willReturn($this->response);
 
        $this->response
            ->expects($this->once())
            ->method('getBody')
            ->willReturn($this->streamedResponse);

        $this->streamedResponse
            ->expects($this->once())
            ->method('getContents')
            ->willReturn('foo');

        $userData = [
            'login' => 'a login',
            'name' => 'user name',
            'email' => 'adress@mail.com',
            'avatar_url' => 'url to the avatar',
            'html_url' => 'url to profile'
        ];
        $this->serializer
            ->expects($this->once())
            ->method('deserialize')
            ->willReturn($userData);

                      $githubUserProvider = new GithubUserProvider($this->client, $this->serializer);
                                        $user = $githubUserProvider->loadUserByUsername('an-access-token');

                $expectedUser = new User($userData['login'], $userData['name'], $userData['email'], $userData['avatar_url'], $userData['html_url']);
        $this->assertEquals($expectedUser, $user);
        $this->assertEquals('App\Entity\User', get_class($user));
    }

    public function testLoadUserByUsernameThrowingException()
    {
        $this->client
            ->expects($this->once())
            ->method('get')
            ->willReturn($this->response);

        $this->response
            ->expects($this->once())
            ->method('getBody')
            ->willReturn($this->streamedResponse);

        $this->streamedResponse
            ->expects($this->once())
            ->method('getContents')
            ->willReturn('foo');

        $this->serializer
            ->expects($this->once())
            ->method('deserialize')
            ->willReturn([]);

        $this->expectException('LogicException');

                      $githubUserProvider = new GithubUserProvider($this->client, $this->serializer);
                      
$githubUserProvider->loadUserByUsername('an-access-token');

    }
}

La méthode  tearDown

Il est également possible d'intervenir à chaque fin de test (après chaque méthode de test de la classe) grâce à la méthode  tearDown  .

Pour cela, il vous faut mettre les propriétés à  null  à nouveau :

<?php
namespace App\Tests\Security;
// …
class GithubUserProviderTest extends TestCase
{
    // …
    public function tearDown() : void
    {
        $this->client = null;
        $this->serializer = null;
        $this->streamedResponse = null;
        $this->response = null;
    }
}

En résumé

  • Faire attention à ce que l’on double, afin de bien maîtriser nos doublures pour contrôler leur comportement de A à Z.

  • Un test unitaire peut prendre du temps au début, mais en fait gagner beaucoup par la suite.

  • Tester son code permet d'appréhender une première fois un code inconnu.

Nous en avons fini avec cette première partie ! Après le quiz, rendez-vous au premier chapitre de la partie 2, nous répondrons aux questions que vous devez vous poser lorsque vous écrivez des tests unitaires.

Exemple de certificat de réussite
Exemple de certificat de réussite