Prev Next

Chapitre 19. Etendre PHPUnit

PHPUnit peut être étendu de multiples façon pour rendre l'écriture des tests plus facile et personnaliser le retour que vous obtenez des tests exécutés. Voici les points de départs communs pour étendre PHPUnit.

Sous-classe PHPUnit_Framework_TestCase

Ecrivez des assertions personnalisées et des méthodes utilitaires dans une sous classe abstraite de PHPUnit_Framework_TestCase et faites hériter vos classes de cas de test de cette classe. C'est une des façon les plus faciles pour étendre PHPUnit.

Ecrire des assertions personnalisées

Lorsqu'on écrit des assertions personnalisées, une bonne pratique consiste à suivre la façon dont PHPUnit implémente ses propres assertions. Comme vous pouvez le voir dans Exemple 19.1, « Les méthodes assertTrue() et isTrue() de la classe PHPUnit_Framework_Assert », la méthode assertTrue() n'est qu'un enrobeur des méthodes isTrue() et assertThat(): isTrue() crée un objet de correspondance qui est passé à assertThat() pour évaluation.

Exemple 19.1. Les méthodes assertTrue() et isTrue() de la classe PHPUnit_Framework_Assert

<?php
abstract class PHPUnit_Framework_Assert
{
// ...

/**
* Asserts that a condition is true.
*
* @param boolean $condition
* @param string $message
* @throws PHPUnit_Framework_AssertionFailedError
*/
public static function assertTrue($condition, $message = '')
{
self::assertThat($condition, self::isTrue(), $message);
}

// ...

/**
* Returns a PHPUnit_Framework_Constraint_IsTrue matcher object.
*
* @return PHPUnit_Framework_Constraint_IsTrue
* @since Method available since Release 3.3.0
*/
public static function isTrue()
{
return new PHPUnit_Framework_Constraint_IsTrue;
}

// ...
}?>

Exemple 19.2, « La classe PHPUnit_Framework_Constraint_IsTrue » montre comment PHPUnit_Framework_Constraint_IsTrue étend la classe abstraite de base pour des objets de correspondance (ou des contraintes), PHPUnit_Framework_Constraint.

Exemple 19.2. La classe PHPUnit_Framework_Constraint_IsTrue

<?php
class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint
{
/**
* Evaluates the constraint for parameter $other. Returns TRUE if the
* constraint is met, FALSE otherwise.
*
* @param mixed $other Value or object to evaluate.
* @return bool
*/
public function evaluate($other)
{
return $other === TRUE;
}

/**
* Returns a string representation of the constraint.
*
* @return string
*/
public function toString()
{
return 'is true';
}
}?>

L'effort d'implémentation des méthodes assertTrue() et isTrue() ainsi que la classe PHPUnit_Framework_Constraint_IsTrue tire bénéfice du fait que assertThat() prend automatiquement soin d'évaluer l'assertion et les tâches de suivi comme le décompte à des fins de statistique. Plus encore, la méthode isTrue() peut être utilisée comme un matcher lors de la configuration d'objets simulacres.

Implémenter PHPUnit_Framework_TestListener

Exemple 19.3, « Un simple moniteur de test » montre une implémentation simple de l'interface PHPUnit_Framework_TestListener.

Exemple 19.3. Un simple moniteur de test

<?php
class SimpleTestListener implements PHPUnit_Framework_TestListener
{
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Error while running test '%s'.\n", $test->getName());
}

public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
{
printf("Test '%s' failed.\n", $test->getName());
}

public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' is incomplete.\n", $test->getName());
}

public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' has been skipped.\n", $test->getName());
}

public function startTest(PHPUnit_Framework_Test $test)
{
printf("Test '%s' started.\n", $test->getName());
}

public function endTest(PHPUnit_Framework_Test $test, $time)
{
printf("Test '%s' ended.\n", $test->getName());
}

public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("TestSuite '%s' started.\n", $suite->getName());
}

public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("TestSuite '%s' ended.\n", $suite->getName());
}
}
?>

Dans la section intitulée « Moniteurs de tests » vous pouvez voir comment configurer PHPUnit pour brancher votre moniteur de test lors de l'exécution des tests.

Sous classer PHPUnit_Extensions_TestDecorator

Vous pouvez encapsuler des cas de test ou des séries de tests dans une sous-classe de PHPUnit_Extensions_TestDecorator et utiliser le Design Pattern Decorator pour réaliser certaines actions avant et après que les tests sont exécutés.

PHPUnit apporte deux décorateurs de test concrets: PHPUnit_Extensions_RepeatedTest et PHPUnit_Extensions_TestSetup. Le premier est utilisé pour exécuter de manière répétée un test et ne le comptabiliser comme succès que si toutes les itérations ont réussi. Le second est discuté dans Chapitre 6, Fixtures.

Exemple 19.4, « Le décorateur RepeatedTest » montre une version raccourcie du décorateur de test PHPUnit_Extensions_RepeatedTest qui illustre comment écrire vos propres décorateurs de tests.

Exemple 19.4. Le décorateur RepeatedTest

<?php
require_once 'PHPUnit/Extensions/TestDecorator.php';

class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;

public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test);

if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}

public function count()
{
return $this->timesRepeat * $this->test->count();
}

public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = $this->createResult();
}

for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
$this->test->run($result);
}

return $result;
}
}
?>

Implémenter PHPUnit_Framework_Test

L'interface PHPUnit_Framework_Test est restreinte et facile à implémenter. Vous pouvez écrire une implémentation de PHPUnit_Framework_Test qui est plus simple que PHPUnit_Framework_TestCase et qui exécute des tests dirigés par les données, par exemple.

Exemple 19.5, « Un test dirigé par les données » montre une classe de cas de test dirigé par les tests qui compare les valeurs d'un fichier contenant des valeurs séparées par des virgules (CSV). Chaque ligne d'un tel fichier ressemble à foo;bar, où la première valeur est celle que nous attendons et la seconde valeur celle constatée.

Exemple 19.5. Un test dirigé par les données

<?php
class DirigeParLesDonneesTest implements PHPUnit_Framework_Test
{
private $lines;

public function __construct($dataFile)
{
$this->lines = file($dataFile);
}

public function count()
{
return 1;
}

public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = new PHPUnit_Framework_TestResult;
}

foreach ($this->lines as $line) {
$result->startTest($this);
PHP_Timer::start();
$stopTime = NULL;

list($expected, $actual) = explode(';', $line);

try {
PHPUnit_Framework_Assert::assertEquals(
trim($expected), trim($actual)
);
}

catch (PHPUnit_Framework_AssertionFailedError $e) {
$stopTime = PHP_Timer::stop();
$result->addFailure($this, $e, $stopTime);
}

catch (Exception $e) {
$stopTime = PHP_Timer::stop();
$result->addError($this, $e, $stopTime);
}

if ($stopTime === NULL) {
$stopTime = PHP_Timer::stop();
}

$result->endTest($this, $stopTime);
}

return $result;
}
}

$test = new DataDrivenTest('fichier_donnees.csv');
$resultat = PHPUnit_TextUI_TestRunner::run($test);
?>
PHPUnit 3.8.0 by Sebastian Bergmann.

.F

Time: 0 seconds

There was 1 failure:

1) DirigeParLesDonneesTest
Failed asserting that two strings are equal.
expected string <bar>
difference      <  x>
got string      <baz>
/home/sb/DirigeParLesDonneesTest.php:32
/home/sb/DirigeParLesDonneesTest.php:53

FAILURES!
Tests: 2, Failures: 1.

Prev Next
1. Automatiser les tests
2. Objectifs de PHPUnit
3. Installer PHPUnit
4. Ecrire des tests pour PHPUnit
Dépendances des tests
Fournisseur de données
Tester des exceptions
Tester les erreurs PHP
Tester la sortie écran
Assertions
assertArrayHasKey()
assertClassHasAttribute()
assertClassHasStaticAttribute()
assertContains()
assertContainsOnly()
assertCount()
assertEmpty()
assertEqualXMLStructure()
assertEquals()
assertFalse()
assertFileEquals()
assertFileExists()
assertGreaterThan()
assertGreaterThanOrEqual()
assertInstanceOf()
assertInternalType()
assertJsonFileEqualsJsonFile()
assertJsonStringEqualsJsonFile()
assertJsonStringEqualsJsonString()
assertLessThan()
assertLessThanOrEqual()
assertNull()
assertObjectHasAttribute()
assertRegExp()
assertStringMatchesFormat()
assertStringMatchesFormatFile()
assertSame()
assertSelectCount()
assertSelectEquals()
assertSelectRegExp()
assertStringEndsWith()
assertStringEqualsFile()
assertStringStartsWith()
assertTag()
assertThat()
assertTrue()
assertXmlFileEqualsXmlFile()
assertXmlStringEqualsXmlFile()
assertXmlStringEqualsXmlString()
5. Le lanceur de tests en ligne de commandes
Options de la ligne de commandes
6. Fixtures
Plus de setUp() que de tearDown()
Variantes
Partager les Fixtures
Etat global
7. Organiser les tests
Composer une suite de tests en utilisant le système de fichiers
Composer une suite de tests en utilisant la configuration XML
8. Tester des bases de données
Systèmes gérés pour tester des bases de données
Difficultés pour tester les bases de données
Les quatre phases d'un test de base de données
1. Nettoyer la base de données
2. Configurer les fixtures
3–5. Exécuter les tests, vérifier les résultats et nettoyer
Configuration d'un cas de test de base de données PHPUnit
Implémenter getConnection()
Implémenter getDataSet()
Qu'en est-il du schéma de base de données (DDL)?
Astuce: utilisez votre propre cas de tests abstrait de base de données
Comprendre DataSets et DataTables
Implémentations disponibles
Attention aux clefs étrangères
Implementer vos propres DataSets/DataTables
L'API de connexion
API d'assertion de base de données
Faire une assertion sur le nombre de lignes d'une table
Faire une assertion sur l'état d'une table
Faire une assertion sur le résultat d'une requête
Faire une assertion sur l'état de plusieurs tables
Foire aux questions
PHPUnit va-t'il (re-)créer le schéma de base de données pour chaque test ?
Suis-je obligé d'utiliser PDO dans mon application pour que l'extension de base de données fonctionne ?
Que puis-je faire quand j'obtiens une erreur « Too much Connections (Trop de connexions) » ?
Comment gérer les valeurs NULL avec les DataSets au format XML à plat / CSV ?
9. Tests incomplets et sautés
Tests incomplets
Sauter des tests
Sauter des tests en utilisant @requires
10. Doublure de test
Bouchons
Objets simulacres (Mock Objects)
Bouchon et simulacre pour Web Services
Simuler le système de fichiers
11. Pratiques de test
Pendant le développement
Pendant le débogage
12. Développement dirigé par les tests
Exemple du compte bancaire
13. Développement dirigé par le comportement
Exemple du jeu de Bowling
14. Analyse de couverture de code
Spécifier les méthodes couvertes
Ignorer des blocs de code
Inclure et exclure des fichiers
Cas limites
15. Autres utilisations des tests
Documentation agile
Tests transverses à l'équipe
16. Générateur de squelette
Générer un squelettre de classe de cas de test
Générer un squelette de classe à partir d'une classe de cas de test
17. PHPUnit et Selenium
Selenium Server
Installation
PHPUnit_Extensions_Selenium2TestCase
PHPUnit_Extensions_SeleniumTestCase
18. Journalisation
Résultats de test (XML)
Résultats de test (TAP)
Résultats de test (JSON)
Couverture de code (XML)
Couverture de code (TEXTE)
19. Etendre PHPUnit
Sous-classe PHPUnit_Framework_TestCase
Ecrire des assertions personnalisées
Implémenter PHPUnit_Framework_TestListener
Sous classer PHPUnit_Extensions_TestDecorator
Implémenter PHPUnit_Framework_Test
A. Assertions
B. Annotations
@author
@backupGlobals
@backupStaticAttributes
@codeCoverageIgnore*
@covers
@coversNothing
@dataProvider
@depends
@expectedException
@expectedExceptionCode
@expectedExceptionMessage
@group
@outputBuffering
@requires
@runTestsInSeparateProcesses
@runInSeparateProcess
@test
@testdox
@ticket
C. Le fichier de configuration Configuration
PHPUnit
Série de tests
Groupes
Inclure et exclure des fichiers de la couverture de code
Journalisation
Moniteurs de tests
Configurer les réglages de PHP INI, les constantes et les variables globales
Configurer les navigateurs pour Selenium RC
D. Index
Index
E. Bibliographie
F. Copyright