Adrien Gallou
Billets

Exécuter des assertions Blackfire dans atoum

Introduction

Depuis la sortie de sa version 2, Blackfire permet d’automatiser les tests de performance.

Cela passe par un SDK PHP qui permet, entre autres, d’intégrer des tests de performance dans PHPUnit : il est par exemple possible de vérifier qu’une portion de code ne dépasse pas une certaine limite de mémoire utilisée, un certain temps d’éxécution ou un certain nombre d’appels d’une fonction/méthode.

Comment peut-on intégrer ces tests dans le framework de tests unitaires atoum et ainsi bénéficier de la facilité d’utilisation de ses asserters ?

Installation

Blackfire aura préalablement été installé, ainsi qu’atoum.

Nous allons tout d’abord récupérer l’extension Blackfire pour atoum via composer.

composer require atoum/blackfire-extension

Il faut ensuite la configurer avec nos client id et client token pour accéder à Blackfire, en rajoutant quelques lignes dans le fichier .atoum.php. Par exemple, ici en les récupérant depuis les variables d’environnement :

$extension = new mageekguy\atoum\blackfire\extension();
$extension
    ->setClientConfiguration(new \Blackfire\ClientConfiguration($_ENV['BLACKFIRE_CLIENT_ID'], $_ENV['BLACKFIRE_CLIENT_TOKEN']))
    ->addToRunner($runner)
;

Utilisation

L’extension va nous rajouter un nouvel asserter : blackfire.

Sur celui-ci, il sera possible d’appeler ces méthodes :

Prenons donc cet exemple de test :

<?php
namespace Tests\Units;

use Example as TestedClass;

use atoum;

class Example extends atoum
{
    public function testExemple()
    {
        $this
            ->blackfire
                ->assert('main.wall_time < 2s')
                ->profile(function() {
                    sleep(4);
                    // appeler le code testé.
                })
        ;
    }
}

Ce test va vérifier que le code appelé dans la fonction anonyme ne prends pas plus de 2 secondes à s’exécuter.

Au vu de l’appel sleep(4), le test ne passera pas et une sortie similaire à celle-ci sera affichée :

Output blackfire atoum

On peut y voir que le code du callback a été exécuté, instrumenté par blackfire, envoyé sur blackfire.io, qui nous a renvoyé les résultats des assertions et ont été intégrées dans les résultats des tests unitaires.

Ceci est un cas basé sur le temps d’exécution. Il est possible de définir des tests plus intéressants comme celui-ci :

<?php
namespace Tests\Units;

use Example as TestedClass;

use atoum;

class Example extends atoum
{
    public function testExemple()
    {
        $this
            ->blackfire
                // ajout d'une métrique sur le nombre d'appels à la méthode foo de la classe `Example`, en lui donnant pour nom `example_foo_calls`. 
                ->defineMetric(new \Blackfire\Profile\Metric("example_foo_calls", "=Example::foo"))

                // ajout d'une assertion (Blackfire) pour vérifier que la méthode foo ne soit pas appellée plus de 10 fois, en utilisant le nom de la métrique précédement créee.
                ->assert("metrics.example_foo_calls.count < 10")

                // lancement de l'intrumentation du code se trouvant dans le callback (en appellant 12 fois la méthode foo afin de faire échouer le test).
                ->profile(function() {
                    $testedClass = new Example();
                    for ($i = 0; $i < 12; $i++) {
                        $testedClass->foo();
                    }
                })
        ;
    }
}

Eviter de lancer ces tests

Afin de choisir quand exécuter ces tests, vous pouvez utiliser conjointement le tag @extensions (voir documentation) et l’extension atoum ruler-extension qui va vous permettre de filtrer sur les tests nécessitant blackfire ou l’inverse

Par exemple il vous sera possible de lancer tous les tests ne nécessitant pas blackfire :

./vendor/bin/atoum -d tests --filter 'not(extensions contains "blackfire")'

Conclusion

De nombreuses métriques et variables sont disponibles de base.

De plus, vous pouvez rajouter vos propres métriques.

N’hésitez pas à rajouter ces assertions dans vos tests unitaires.

Enfin, cette extension a besoin la dernière version (2.5.0) d’atoum pour fonctionner (voir l’article listant les nouveautés).