Rappels d'ordre général sur les tests automatisés (unitaires, fonctionnels, de comportement), suivis d'une introduction à l'écriture de tests unitaires avec PHPUnit. Orienté PHP.
2. Tests automatisés
Tests unitaires (structurels)
S’assurer de l’intégrité et du bon fonctionnement du code source.
Outils : PHPUnit / Atoum
Tests fonctionnels
Vérifier le comportement métier d’une application.
Outils : Selenium
Tests de spécifications (comportement)
Contrôler qu’une application correspond au besoin formulé par le client.
Outils : Behat / PHPSpec
3. Tests unitaires
Les tests unitaires permettent de valider que le code développé est conforme aux intentions du développeur. Une unité
représente une classe et ses méthodes.
Ces tests doivent être répétables, automatiques, indépendants et rapides.
Michaël Feathers définit dans Working Effectively With Legacy Code un test unitaire de la façon suivante :
Il ne communique pas avec la base de données
Il ne communique pas avec d’autres ressources sur le réseau
Il ne manipule pas un ou plusieurs fichiers
Il peut s’exécuter en même temps que les autres tests unitaires
On ne doit pas faire quelque chose de spécial, comme éditer un fichier de configuration, pour l’exécuter
4. Tests fonctionnels
Les tests fonctionnels permettent de tester l’intégration des différentes couches applicatives.
Ils aident également à tester qu’une nouvelle fonctionnalité n’est pas la source d’une régression fonctionnelle.
Workflow standard
Envoi d’une requête HTTP
Test de la réponse
Clic sur un lien ou envoi d’un formulaire
Test de la réponse
Nettoyer et recommencer
5. Tests de spécifications
Les tests de spécifications sont des tests fonctionnels avec un formalisme.
Cette méthodologie a pour objectif d’améliorer la compréhension et la collaboration du métier, du Product Owner, des
développeurs, des testeurs et de toute autre partie prenante.
Les tests d’acceptance sont écrits par les PO, à l’aide du langage Gherkin.
Exemple de description du comportement de la demande :
Scenario: Searching for a page that does exist
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Behavior Driven Development"
And I press "searchButton"
Then I should see "agile software development"
6. PHPUnit
PHPUnit est un framework open source de tests unitaires.
https://github.com/sebastianbergmann/phpunit
Créé en 2004 par Sebastian Bergmann, il est maintenant devenu un standard :
Intégré à de nombreux frameworks
Installable en standalone avec composer
Il est possible d’effectuer des tests fonctionnels avec Symfony (Web Test Case) et d’autres frameworks PHP, comme
Laravel.
PHPUnit utilise massivement des tags PHPDoc personnalisés (de manière toujours optionnelle).
7. Écrire des tests
Assertions principales
- assertTrue()
- assertEquals()
- assertContains()
- assertCount()
- assertEmpty()
- assertInstanceOf()
- assertNull()
Annotations principales
- @expectedException : vérifier qu’une exception est
lancée.
- @dataProvider : fournir plusieurs jeux de données
afin d’éviter la duplication de méthodes de test.
<?php
use PHPUnitFrameworkTestCase;
class DataTest extends TestCase
{
/** @dataProvider additionProvider */
public function testAdd($a, $b, $expected)
{
$this->assertEquals($expected, $a + $b);
}
public function additionProvider()
{
return [
[0, 0, 0],
[0, 1, 1],
[1, 0, 1],
];
}
}
8. Le pattern Mock Object
Le pattern Mock Object permet d’isoler une ressource.
Mock : Faux objet. L’utilisation des mocks permet
également de simuler un appel de fonction plutôt que
l’exécuter. C’est par exemple utile pour simuler une
classe qui fait appel à une librairie extérieure.
Stub : Bouchon. C’est la valeur retournée lors de
l’appel à une méthode d’un objet mocké.
<?php
use PHPUnitFrameworkTestCase;
class StubTest extends TestCase
{
public function testStub()
{
// Create a stub for the SomeClass class.
$stub = $this->getMockBuilder($originalClassName)
->disableOriginalConstructor()
->getMock();
// Configure the stub.
$stub->method('doSomething')
->willReturn('foo');
// Calling $stub->doSomething() will now return 'foo'.
$this->assertEquals('foo', $stub->doSomething());
}
}
9. Base de données et fixtures
Fixtures : jeu de données contrôlées.
Dans un scénario, PHPUnit lance les méthodes dans l'ordre suivant :
1. setUpBeforeClass() : exécuté une fois en début de scénario
2. setUp() : exécuté avant chaque test du scénario : "constructeur"
d'environnement
3. assertPreConditions() : effectue des assertions sur l'état initial de
l'environnement du test à venir
4. testOne() : un premier test
5. testTwo() : un deuxième test
6. assertPostConditions() : effectue des assertions sur l'état de
l'environnement après le lancement d'un test
7. tearDown() : exécuté après chaque test du scénario : "destructeur"
d'environnement
8. tearDownAfterClass() : exécuté une fois en fin de de scénario
<?php
use PHPUnitFrameworkTestCase;
class StackTest extends TestCase {
protected $stack;
protected function setUp() {
$this->stack = [];
}
public function testEmpty() {
$this->assertTrue(empty($this->stack));
}
public function testPush() {
array_push($this->stack, 'foo');
$this->assertEquals('foo', $this->stack[count($this->stack)-1]);
$this->assertFalse(empty($this->stack));
}
public function testPop() {
array_push($this->stack, 'foo');
$this->assertEquals('foo', array_pop($this->stack));
$this->assertTrue(empty($this->stack));
}
}
10. Code coverage
La couverture de code montre le nombre de lignes de code sécurisées par des tests.
PHPunit permet de générer un rapport sur la couverture de code (nécessite l’activation de xdebug), permettant de montrer
les statistiques sur toutes les classes, méthodes et lignes couvertes par des tests.
$ bin/phpunit --coverage-html reports
11. Test Driven Development
Technique de développement logiciel au coeur des méthodes agiles telles que l'extreme programming.
Red - Green - Refactor Cycle
1. Red state : Déclarer et nommer un test, écrire l’assertion, le faire échouer
2. Green state : Faire passer le test au vert le plus tôt possible (quitte à faker)
3. Refactor state : Remanier le code (l’améliorer, le rendre plus lisible)