De nos jours de plus en plus d'entreprises ne jurent que par les tests unitaires. Faire du test, faire du test, faire du test ! “Une application n'est pérenne que si elle est testée et elle est testée si plus de 80% du code est couvert.”
Cela devient même un élément décisif du recruteur en entretien :
- Votre collaborateur a l'air vraiment bien mais... Il a déjà fait des tests unitaires ? Il a plus de deux ans d'expérience là dessus ?
- Juste sur deux projets, par contre il possède la bonne philosophie.
- Ah oui mais non il faut qu'il en ait fait 2 ans, c'est un minimum. On cherche des experts nous !
Problématique : "Je veux minimum 80% de couverture de code !!!" Qui n'a pas entendu cette phrase dans la bouche d'un chef de projet ou d'un lead dev trop consciencieux sans doute.
Dans certains projets un test unitaire est bon si il couvre au moins 80% de la fonctionnalité à tester, c'est tout ce qui est demandé et c'est cela qu'il faut avoir. Il est avant tout essentiel de s'interroger sur la notion de couverture de code dans un test unitaire : La couverture de code est-elle un but ? un facteur qualité ? une représentation visuelle d'un test ? Ou est-ce cet horrible fantôme qui vient hanter une application ?
Pour faire simple : un test qui couvre 100% du code à tester est-il forcement fiable ?
Conférence PHPTour Lyon 2014 - Tests unitaires - Je veux mes 80% de couverture de code !!!! http://afup.org/pages/phptourlyon2014/sessions.php#1094
PHPTour Lyon 2014 - Conférence - Tests unitaires Je veux mes 80% de couverture de code
1. Julien Charpentier – Directeur R&D
Cyrille Grandval – Directeur Général
Tests Unitaires
« Je veux mes 80% de couverture de code »
24.06.2014
PHP Tour Lyon 2014
2. Qui sommes nous?
Cyrille Grandval
Directeur général Darkmira
Consultant sécurité
Auteur
cgrandval@darkmira.fr
@CyrilleGrandval
Julien Charpentier
Directeur R&D
Lead Dev PHP
Auteur
jcharpentier@darkmira.fr
@jcharpentier_
4. Historique des tests unitaires
1994 (K.Beck) : SUnit pour SmallTalk
1997 (K.Beck & E.Gamma) : JUnit pour Java
1999 (K.Beck) : XP => TDD
Multiples frameworks créés (xUnit)
Notamment pour PHP :
PHPUnit (S.Bergmann)
SimpleTest (en fin de vie)
Atoum (F.Hardy)
5. Tests unitaires aujourd’hui…
Préconisés par les techniques de développement logiciel dans lesquels ils sont mis à
l’honneur
• TDD (Tests Driven Development)
• BDD (Behaviour Driven Development)
• PDD (Pragmatism Driven Development)
• merci à Gabriel Pillet :)
Dans les esprits :
• Pratiqués par les développeurs et attendus par les responsables de projets
Dans la réalité :
• Pas le temps
• ROI difficilement quantifiable
• Armada de tests unitaires obsolètes, en échec mais ignorés
• Justification de non écriture systématique
• Fausses pistes de recherche de résolution de bugs
6. Et de plus en plus…
C2DT (Code Coverage Driven Tests)
Je veux au
minimum 80% de
couverture de
code!!!!!!
7.
8. Couverture de code : indicateur quantitatif ou
qualitatif ?
Extrait de wikipedia
“La couverture de code (en anglais code coverage) est une mesure utilisée en génie logiciel
pour décrire le taux de code source testé d'un programme. Ceci permet de mesurer la qualité
des tests effectués.
Car plus un programme est bien testé, moins il est soumis aux bugs.”
Différentes couvertures
Les outils pour PHP nous donne le taux de la couverture des
instructions => QUANTITATIF
• Points de tests
• Chemins d’exécution
Quantitative
• Fonctions
• Instructions
Qualitative
10. Une invitation aux mauvaises pratiques
Que font la plupart des développeurs?
Couverture de code : un trompe l’œil ?
Je test un
comportement
Je passe à un test
d’un autre
comportement
NON
OUI
J’écris un test
Je vérifie la
couverture
>80%
…
11. Couverture de code : un trompe l’œil ?
class Bar {
public function drink($a, $b)
{
$x = '';
if (1 == $b) {
$x .= 'B';
} else {
$x .= 'E';
}
if (1 == $b) {
$x .= 'R';
} else {
$x .= 'E';
}
return $x;
}
}
class BarTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
parent::setUp ();
$this->Bar = new Bar();
}
public function testBar()
{
$sDrink = $this->Bar->drink(1, 2);
$this->assertEquals('BE', $sDrink);
$sDrink = $this->Bar->drink(2, 1);
$this->assertEquals('ER', $sDrink); }
}
14. Couverture de code : un trompe l’œil ?
100% de couverture de code = application testée
N’assure pas que :
• Des assertions sont présentes
• Qu’elles testent les bons comportements
• Que les tests soient vraiment unitaires
• Que le test soit lisible et bien écrit
• Que les cas d’entrée soient clairement identifiés
Que le test soit un test unitaire
15. Couverture de code : conséquences
Une plaie pour les développeurs
• Ils ne comprennent pas l’utilité
• Ils ne comprennent pas comment écrire les tests
• Ils ont l’impression de perdre leur temps
C’est inutile et contre-productif
• Laisser-aller
• Incompréhension
• Perte de temps
• Builds trop longs
Une bombe à retardement
• Coûts de développement trop élevés
• Coûts de maintenance trop élevés
• Perte de confiance
• TU obsolètes et ignorés
• Application vulnérable
18. C2DT : Tue la politique de tests unitaires
Outils pas assez matures pour PHP
Indicateur uniquement quantitatif
N’atteste pas de la qualité des tests unitaires
=> La vérité est ailleurs
21. La qualité à l’état naturel
• Philosophie
• Bonnes pratiques
o Conventions
o Lisibilité
o Commentaires
• Bons outils
22. La qualité à l’état naturel :
Intro
Qu’est ce qu’un test unitaire ?
• Un test unitaire consiste à isoler un comportement de tout facteur extérieur et de
vérifier qu’il est conforme à ce que vous attendez.
Pourquoi teste-t-on ?
• Vérifier
Quels sont les avantages ?
• Documenter
• Automatiser
Sérénité
Ne plus avoir peur de modifier le code
Comprendre un code que l’on n’a pas
écrit
Supprimer du code sans perdre de
fonctionnalité
Facilité de la
maintenance
S’assurer de la non régression
23. La qualité à l’état naturel :
Philosophie
Coder en pensant test…
• Coder l’application pour qu’elle soit naturellement et unitairement testable
• Veiller à garder un couplage faible
• Utiliser l’injection de dépendance
• Veiller à limiter la complexité cyclomatique
• Faire des revues de codes ou du pair programming
• Ne pas propager un code qui peut-être refactorisé
…et tester en pensant qualité de la couverture
• Rechercher le taux de couverture des chemins d’exécution le plus haut possible
• Tester en boîte blanche
o Identifier et tester toutes les entrées possibles…
o …et vérifier les sorties attendues correspondantes
• Ne pas se limiter aux cas simples
o Tester les cas nominaux…
o …mais aussi les cas en erreur et les cas limites
24. La qualité à l’état naturel :
Bonnes pratiques
10 règles à respecter
• Un test unitaire doit-être UNITAIRE !
• Utilisez les mocks et/ou stubs (100% des appels externes)
• Un test est avant tout du code : il doit simple, normé, commenté, facile à lire et à
comprendre
• Un test doit être rapide à exécuter
• Les tests doivent être écrits le plus tôt possible
• Un test = une assertion (ou presque)
• Testez en priorité les parties critiques de l’application
• Écrivez le code de l’application pour qu’il soit testable
• Refactorisez !
• Le nom d’un test est capital, normalisez le !
ex: testNomMethode_EtatDepart_ComportementAttendu donnera
testOpenFile_FileNoExists_ThrowException()
25. La qualité à l’état naturel :
Bonnes pratiques
10 règles à respecter
• Un test unitaire doit-être UNITAIRE !
• Utilisez les mocks et/ou stubs (100% des appels externes)
• Un test est avant tout du code : il doit simple, normé, commenté, facile à lire et à
comprendre
• Un test doit être rapide à exécuter
• Les tests doivent être écrits le plus tôt possible
• Un test = une assertion (ou presque)
• Testez en priorité les parties critiques de l’application
• Écrivez le code de l’application pour qu’il soit testable
• Refactorisez !
• Le nom d’un test est capital, normalisez le !
26. La qualité à l’état naturel :
Bonnes pratiques
10 règles à respecter
• Un test unitaire doit-être UNITAIRE !
• Utilisez les mocks et/ou stubs (100% des appels externes)
• Un test est avant tout du code : il doit simple, normé, commenté, facile à lire et à
comprendre
• Un test doit être rapide à exécuter
• Les tests doivent être écrits le plus tôt possible
• Un test = une assertion (ou presque)
• Testez en priorité les parties critiques de l’application
• Écrivez le code de l’application pour qu’il soit testable
• Refactorisez !
• Le nom d’un test est capital, normalisez le !
27. La qualité à l’état naturel :
Bonnes pratiques
10 règles à respecter
• Un test unitaire doit-être UNITAIRE !
• Utilisez les mocks et/ou stubs (100% des appels externes)
• Un test est avant tout du code : il doit simple, normé, commenté, facile à lire et à
comprendre
• Un test doit être rapide à exécuter
• Les tests doivent être écrits le plus tôt possible
• Un test = une assertion (ou presque)
• Testez en priorité les parties critiques de l’application
• Écrivez le code de l’application pour qu’il soit testable
• Refactorisez !
• Le nom d’un test est capital, normalisez le !
28. La qualité à l’état naturel :
Bonnes pratiques
10 règles à respecter
• Un test unitaire doit-être UNITAIRE !
• Utilisez les mocks et/ou stubs (100% des appels externes)
• Un test est avant tout du code : il doit simple, normé, commenté, facile à lire et à
comprendre
• Un test doit être rapide à exécuter
• Les tests doivent être écrits le plus tôt possible
• Un test = une assertion (ou presque)
• Testez en priorité les parties critiques de l’application
• Écrivez le code de l’application pour qu’il soit testable et refactorisez !
• Utiliser les design pattern (ex : AAA)
• Le nom d’un test est capital, normalisez le !
test[NomMethode][EtatDeDepart][ComportementAttendu]
29.
30.
31. 10 pièges à éviter
• Avoir pour but la couverture de code :)
• Écrire un test illisible
• Écrire un test non isolé
o dépendant de facteurs extérieurs (temps)
o dépendant de l'environnement (système de fichiers, base de données,
webservices, ...)
o dépendant d’un autre test
• Écrire les tests quand on a le temps
• Décorréler le test et le code testé
o Ecrire un test sans comprendre le comportement testé
o Écrire un test sans refactoriser un code qui doit l’être
• Confondre test unitaire et test d’intégration
• Ne pas exécuter les TU à chaque commit / push sur le dépot
• Écrire un test par comportement testé
• Ecrire un test gérant plusieurs états
• Faire trop de tests unitaires !
La qualité à l’état naturel :
Mauvaises pratiques
32. 10 pièges à éviter
• Avoir pour but la couverture de code :)
• Écrire un test illisible
• Écrire un test non isolé
o dépendant de facteurs extérieurs (temps)
o dépendant de l'environnement (système de fichiers, base de données,
webservices, ...)
o dépendant d’un autre test
• Écrire les tests quand on a le temps
• Décorréler le test et le code testé
o Ecrire un test sans comprendre le comportement testé
o Écrire un test sans refactoriser un code qui doit l’être
• Confondre test unitaire et test d’intégration
• Ne pas exécuter les TU à chaque commit / push sur le dépot
• Écrire un test par comportement testé
• Ecrire un test gérant plusieurs états
• Faire trop de tests unitaires !
La qualité à l’état naturel :
Mauvaises pratiques
33. 10 pièges à éviter
• Avoir pour but la couverture de code :)
• Écrire un test illisible
• Écrire un test non isolé
o dépendant de facteurs extérieurs (temps)
o dépendant de l'environnement (système de fichiers, base de données,
webservices, ...)
o dépendant d’un autre test
• Écrire les tests quand on a le temps
• Décorréler le test et le code testé
o Ecrire un test sans comprendre le comportement testé
o Écrire un test sans refactoriser un code qui doit l’être
• Confondre test unitaire et test d’intégration
• Ne pas exécuter les TU à chaque commit / push sur le dépot
• Écrire un test par comportement testé
• Ecrire un test gérant plusieurs états
• Faire trop de tests unitaires !
La qualité à l’état naturel :
Mauvaises pratiques
34. 10 pièges à éviter
• Avoir pour but la couverture de code :)
• Écrire un test illisible
• Écrire un test non isolé
o dépendant de facteurs extérieurs (temps)
o dépendant de l'environnement (système de fichiers, base de données,
webservices, ...)
o dépendant d’un autre test
• Écrire les tests quand on a le temps
• Décorréler le test et le code testé
o Ecrire un test sans comprendre le comportement testé
o Écrire un test sans refactoriser un code qui doit l’être
• Confondre test unitaire et test d’intégration
• Ne pas exécuter les TU à chaque commit / push sur le dépot
• Écrire un test par comportement testé
• Ecrire un test gérant plusieurs états
• Faire trop de tests unitaires !
La qualité à l’état naturel :
Mauvaises pratiques
36. Un peu d’histoire
• 1971 (Richard Lipton) – Concept
• 1980 (Timothy Budd) - Premier outil
• Constat : Les tests vérifient que l’application est l'implémentation correcte des
besoins. Sont-ils suffisamment qualifiés ? Repèrent-t-ils un dysfonctionnement ?
• Objectif : Créer des mutations simples du code source d’une application et
soumettre ces mutations à la suite de tests unitaires.
• Score qualitatif = nombre de mutants tués / nombre de mutants créés
Tester la qualité :
le Mutation Testing
37. Fonctionnement
• Le mutation-testing utilise un lot de mutations simples qu’il applique une à une sur le
code source original de l’application.
• Il crée ainsi des mutations du code original qui en modifie son comportement.
• Ce comportement étant différent, certains tests devraient logiquement le repérer et
ne plus être acceptés
• Exemples de mutations :
o modification de conditions (== par !=, == par =, && par ||, > par <, etc.)
o modification structurelles (suppression de bloc else, d’un case de switch, etc.)
o modification de valeurs (true par false, …)
Tester la qualité :
le Mutation Testing
38. Tester la qualité :
le Mutation Testing
Exemple 1
if ($a && $b) {
$c = 1;
} else {
$c = 0;
}
L’opérateur de
condition && va être
remplacé par ||
if ($a || $b) {
$c = 1;
} else {
$c = 0;
}
Si $a = true et $b = false, le
comportement est différent
du code original, $c sera égal
à 1 au lieu d’être à 0.
Un test unitaire va-t-il
repérer cela ?
39. Tester la qualité :
le Mutation Testing
Exemple 2
if ($a && $b) {
$c = 1;
} else {
$c = 0;
}
Le bloc else va être
supprimé
if ($a && $b) {
$c = 1;
}
Si $a et/ou $b sont égaux à
false, le comportement est
différent du code original, $c
ne sera pas défini au lieu d’être
égal à 0.
Un test unitaire va-t-il repérer
cela ?
40. Tester la qualité :
le Mutation Testing
Ce qui impose donc au test, pour tuer le mutant :
• de couvrir l’instruction mutante
• de prendre en compte l’entrée qui donne vie au mutant
• d’être capable de vérifier que la sortie mutante n’est pas attendue
et impose au code, de propager la ou les valeurs mutantes
en sortie.
Cela vérifie donc bien :
• la couverture des instructions
• la couverture des chemins d’exécution
• la couverture des points de tests
• la couverture des E/S
41. Tester la qualité :
le Mutation Testing
Outils pour PHP
• 2013 (Jean-François Lépine) MutaTesting
o PHP 5.3+
o Ne nécessite pas d’extension
o Indépendant du framework de tests
o Ne nécessite pas de code en plus
o Intégrable facilement dans un outil d’intégration continue (type Jenkins)
o Ne crée pas les mutants physiquement
(Source : https://github.com/Halleck45/MutaTesting/)
Les conséquences de penser test unitaires purement en code coverage c’est une plaie pour les développeurs, contre-productif et une bombe à retardement
Roue de Deming et l’amélioration continue utilisée notamment pour la qualité des entreprises privées japonaises
4 actions, Prévoir, Faire, Vérifier, Réagir et ainsi de suite avec une cale d’expérience qui empêche de ne pas avancer