PHPTour Lyon 2014 - Conférence - Tests unitaires Je veux mes 80% de couvertur...Cyrille Grandval
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
Le test, qu'il soit unitaire ou fonctionnel, est à la mode dans le monde du développement logiciel, suite entre autre à la mise en œuvre croissante des méthodes agiles et notamment de l'intégration continue ou des méthodes de développement telles que le TDD, le BDD ou la programmation par contrat. Récemment, ce phénomène a encore été amplifié au sein de la communauté PHP par l'apparition aux côtés de l'incontournable PHPUnit d'outils plus originaux tels que Behat, Praspel ou atoum qui permettent au développeur de rédiger des tests plus simplement. Pourtant, nous constatons tous les jours que le test conserve une grande part de mystère pour la plupart des développeurs, Bien souvent, ces derniers ne savent pas quoi tester, et encore moins comment écrire un test efficace ou mettre en place une politique de test pertinente. Certains s'interrogent par exemple sur la pertinence de leurs tests, se demandent s'il faut absolument tout tester, d'autres s'il est possible de tester la création d'un fichier, voir même s'il est intéressant de le faire, tandis que d'autres se demandent où se situe la frontière entre le test unitaire et le test fonctionnel ou s'il est nécessaire de tester toutes les méthodes d'une classe, alors que d'autres encore ne savent tout simplement pas par où commencer. Durant cette conférence, nous allons tenter, à l'aide de nos expériences respectives de créateur de framework de tests et de doctorat en informatique spécialisé dans le test, de répondre aux questions récurrentes que se pose une personne confrontée à la mise en place d'une politique de qualité logicielle en général et à l'écriture d'un test logiciel en particulier. À l'issue de cette foire aux questions didactique et interactive, vous devriez être capable d'aborder le test, indépendamment de sa nature, de manière plus sereine et efficace et produire ainsi un logiciel de la qualité que vous désirez.
Rédigé en Mars 2013
Comment automatiser les tests ?
Les différents types de tests automatisés : TU, BDD/TDD, GUI, TDC, Test de vie …
Méthodes d’automatisation
Capture/replay
Projet de développement
Techniques d’automatisation
Data driven
Keyword driven
DSTL
Composants technique pour l’automatisation
Oracle
Bouchon
Techniques de comparaison
Reporting
Dans nos accompagnements techniques, nous observons régulièrement des problèmes de Legacy Code aussi appelé Code Patrimonial. Notamment lorsque des équipes font un virage agile et on leur demande soudainement de faire des tests unitaires automatisés. Pas si facile que cela.
Dans cette présentation, nous verrons les points suivants:
- Description de quelques techniques pour nous aider à tester le Legacy Code
- Comment avoir le droit de travailler sur du code pour le rendre plus facile à travailler
- Quelques pratiques et outils afin de s'en prémunir autant que possible au jour le jour.
Cette présentation a été donnée aux dates suivantes:
- 10 Novembre 2016 - Beer And Learn (Québec)
- 16 Novembre 2016 - Agile Tour Montréal
S'il est facile de comprendre l'intérêt d'un code bien testé, la mise en œuvre de tests se heurte souvent au problème des dépendances du code testé : comment s'abstraire de ces dépendances ?
A travers une présentation pratique, où Stéphane Malbéqui, Yannick Ameur et Anthony Dahanne rencontreront et résoudrons plusieurs obstacles à la mise en oeuvre de tests untiataires, vous découvrirez à travers un cas concret la mise en œuvre de TDD !
PHPTour Lyon 2014 - Conférence - Tests unitaires Je veux mes 80% de couvertur...Cyrille Grandval
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
Le test, qu'il soit unitaire ou fonctionnel, est à la mode dans le monde du développement logiciel, suite entre autre à la mise en œuvre croissante des méthodes agiles et notamment de l'intégration continue ou des méthodes de développement telles que le TDD, le BDD ou la programmation par contrat. Récemment, ce phénomène a encore été amplifié au sein de la communauté PHP par l'apparition aux côtés de l'incontournable PHPUnit d'outils plus originaux tels que Behat, Praspel ou atoum qui permettent au développeur de rédiger des tests plus simplement. Pourtant, nous constatons tous les jours que le test conserve une grande part de mystère pour la plupart des développeurs, Bien souvent, ces derniers ne savent pas quoi tester, et encore moins comment écrire un test efficace ou mettre en place une politique de test pertinente. Certains s'interrogent par exemple sur la pertinence de leurs tests, se demandent s'il faut absolument tout tester, d'autres s'il est possible de tester la création d'un fichier, voir même s'il est intéressant de le faire, tandis que d'autres se demandent où se situe la frontière entre le test unitaire et le test fonctionnel ou s'il est nécessaire de tester toutes les méthodes d'une classe, alors que d'autres encore ne savent tout simplement pas par où commencer. Durant cette conférence, nous allons tenter, à l'aide de nos expériences respectives de créateur de framework de tests et de doctorat en informatique spécialisé dans le test, de répondre aux questions récurrentes que se pose une personne confrontée à la mise en place d'une politique de qualité logicielle en général et à l'écriture d'un test logiciel en particulier. À l'issue de cette foire aux questions didactique et interactive, vous devriez être capable d'aborder le test, indépendamment de sa nature, de manière plus sereine et efficace et produire ainsi un logiciel de la qualité que vous désirez.
Rédigé en Mars 2013
Comment automatiser les tests ?
Les différents types de tests automatisés : TU, BDD/TDD, GUI, TDC, Test de vie …
Méthodes d’automatisation
Capture/replay
Projet de développement
Techniques d’automatisation
Data driven
Keyword driven
DSTL
Composants technique pour l’automatisation
Oracle
Bouchon
Techniques de comparaison
Reporting
Dans nos accompagnements techniques, nous observons régulièrement des problèmes de Legacy Code aussi appelé Code Patrimonial. Notamment lorsque des équipes font un virage agile et on leur demande soudainement de faire des tests unitaires automatisés. Pas si facile que cela.
Dans cette présentation, nous verrons les points suivants:
- Description de quelques techniques pour nous aider à tester le Legacy Code
- Comment avoir le droit de travailler sur du code pour le rendre plus facile à travailler
- Quelques pratiques et outils afin de s'en prémunir autant que possible au jour le jour.
Cette présentation a été donnée aux dates suivantes:
- 10 Novembre 2016 - Beer And Learn (Québec)
- 16 Novembre 2016 - Agile Tour Montréal
S'il est facile de comprendre l'intérêt d'un code bien testé, la mise en œuvre de tests se heurte souvent au problème des dépendances du code testé : comment s'abstraire de ces dépendances ?
A travers une présentation pratique, où Stéphane Malbéqui, Yannick Ameur et Anthony Dahanne rencontreront et résoudrons plusieurs obstacles à la mise en oeuvre de tests untiataires, vous découvrirez à travers un cas concret la mise en œuvre de TDD !
Le TDD (Test Driven Development) devient de plus en plus populaire. Guider le développement d’une application par les tests est maintenant une pratique acceptée dans plusieurs communautés. Mais démarrer avec le TDD peut être une tâche ardue.
Dans cette présentation, nous allons voir ce qu’est le TDD. En commençant par les principes fondamentaux au TDD. Puis nous verrons quels sont les outils utilisés. Puis comment appliquer ces techniques dans le travail quotidien.
[Agile Testing Day] Test Driven Development (TDD)Cellenza
Soyons honnête : nous aimerions tous tester nos plateformes, nos codes, mais personne ne le fait vraiment bien. Heureusement, ce n’est pas une fatalité, et il n’est jamais trop tard pour tester ! La vraie question est : comment tester ? Derrière toute stratégie de tests efficace, il y a une connaissance de tous les types de tests disponibles, de leurs coûts et de leurs utilités. Tout au long de cette journée, nous allons vous détailler les différents types de tests, du test unitaire au test de charge, afin que vous puissiez évaluer la pertinence de chacun dans votre propre contexte.
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testableAssociation Agile Nantes
Les tests unitaires automatisés sont indispensables à l'agilité. Le TDD est le meilleur moyen d'écrire
ces tests et d'avoir du code testable, mais sa pratique va au-delà, notamment dans l'aide à la
conception du code. Un peu de théorie et beaucoup de démo live pour vous montrer cette pratique.
Rédigé en Mars 2013
Introduction : ce que l’on va couvrir (et ne pas couvrir)
Définition : Qu’est-ce que l’automatisation des tests ?
Objectifs : Pourquoi automatiser ?
Couverture :
Qu’est-ce qu’on automatise ?
Pre et Post Process
Comment déterminer ce qu’on automatise ?
Responsabilité : Qui fait quoi?
ROI : Combien ça coute ?
Infrastructure de test
Processus d’automatisation
Conclusion
Trop souvent tester son code se fait soit en fin de projet, soit pas du tout ; et correspond à une contrainte pour les développeurs. C’est pourtant à la fois un confort et une assurance dans la qualité et de son travail. A travers cette présentation, “tester c’est douter”, j’ai voulu provoquer l’envie de s’y mettre. Expliquer comment les tests amènent à remettre correctement son travail en cause.
Pour ce faire j’ai choisi une approche agnostique, n’évoquant ni les technos ni la manière de les utiliser.
J’ai privilégié de présenter plutôt la logique des tests :
Expliquer ce qu’est un test, les différents types et quand les utiliser.
Evoquer le pattern des tests doubles et différencier un mock d’un stub. Présenter une série de bonnes pratiques pour coder correctement ses tests et éviter les tests smells
Présentation d’un cas pratique pour appuyer la théorie
Présentation des patterns TDD et BDD pour introduire les tests au coeur de ces développements.
L’université de la performance vous fera découvrir comment concevoir la plus grosse fonctionnalité implicite d’une application: Sa performance.
Pour cela nous vous proposerons une démarche en trois étapes: - Connaître les différents types de tests de charge et savoir quand les utiliser - Mettre en place un test de charge et des outils nécessaires pour le monitoring - Savoir identifier et optimiser les différents goulets d’étranglement de l’application
Le tout mis en pratique sur une application réelle.
Le TDD (Test Driven Development) devient de plus en plus populaire. Guider le développement d’une application par les tests est maintenant une pratique acceptée dans plusieurs communautés. Mais démarrer avec le TDD peut être une tâche ardue.
Dans cette présentation, nous allons voir ce qu’est le TDD. En commençant par les principes fondamentaux au TDD. Puis nous verrons quels sont les outils utilisés. Puis comment appliquer ces techniques dans le travail quotidien.
[Agile Testing Day] Test Driven Development (TDD)Cellenza
Soyons honnête : nous aimerions tous tester nos plateformes, nos codes, mais personne ne le fait vraiment bien. Heureusement, ce n’est pas une fatalité, et il n’est jamais trop tard pour tester ! La vraie question est : comment tester ? Derrière toute stratégie de tests efficace, il y a une connaissance de tous les types de tests disponibles, de leurs coûts et de leurs utilités. Tout au long de cette journée, nous allons vous détailler les différents types de tests, du test unitaire au test de charge, afin que vous puissiez évaluer la pertinence de chacun dans votre propre contexte.
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testableAssociation Agile Nantes
Les tests unitaires automatisés sont indispensables à l'agilité. Le TDD est le meilleur moyen d'écrire
ces tests et d'avoir du code testable, mais sa pratique va au-delà, notamment dans l'aide à la
conception du code. Un peu de théorie et beaucoup de démo live pour vous montrer cette pratique.
Rédigé en Mars 2013
Introduction : ce que l’on va couvrir (et ne pas couvrir)
Définition : Qu’est-ce que l’automatisation des tests ?
Objectifs : Pourquoi automatiser ?
Couverture :
Qu’est-ce qu’on automatise ?
Pre et Post Process
Comment déterminer ce qu’on automatise ?
Responsabilité : Qui fait quoi?
ROI : Combien ça coute ?
Infrastructure de test
Processus d’automatisation
Conclusion
Trop souvent tester son code se fait soit en fin de projet, soit pas du tout ; et correspond à une contrainte pour les développeurs. C’est pourtant à la fois un confort et une assurance dans la qualité et de son travail. A travers cette présentation, “tester c’est douter”, j’ai voulu provoquer l’envie de s’y mettre. Expliquer comment les tests amènent à remettre correctement son travail en cause.
Pour ce faire j’ai choisi une approche agnostique, n’évoquant ni les technos ni la manière de les utiliser.
J’ai privilégié de présenter plutôt la logique des tests :
Expliquer ce qu’est un test, les différents types et quand les utiliser.
Evoquer le pattern des tests doubles et différencier un mock d’un stub. Présenter une série de bonnes pratiques pour coder correctement ses tests et éviter les tests smells
Présentation d’un cas pratique pour appuyer la théorie
Présentation des patterns TDD et BDD pour introduire les tests au coeur de ces développements.
L’université de la performance vous fera découvrir comment concevoir la plus grosse fonctionnalité implicite d’une application: Sa performance.
Pour cela nous vous proposerons une démarche en trois étapes: - Connaître les différents types de tests de charge et savoir quand les utiliser - Mettre en place un test de charge et des outils nécessaires pour le monitoring - Savoir identifier et optimiser les différents goulets d’étranglement de l’application
Le tout mis en pratique sur une application réelle.
2. ❑ Introduction
❑ Méthodes traditionnelles et tests
❑ Impacts et coût des défauts
❑ Test Driven Development
❑ Définition
❑ Méthodologie
❑ Avantages, inconvénients
❑ Injection de dépendances, fakes et mocks
❑ Exemple
❑ Expérience personnelle
Introduction
2
4. ❑ Le code logiciel est fragile
❑ Quasiment n’importe quel changement peut avoir des conséquences
inattendues
❑ Le but de cette présentation est de montrer une méthode de
développement qui permet d'améliorer la qualité logiciel, et de réduire
les coûts de développement
❑ Grâce à cette méthode, nous pourrons à tout moment nous assurer de la
maturité du logiciel, et être immédiatement averti de toute déviation,
régression ou défaut, et ce dès leur introduction dans le code.
Programmation Pilotée par les Tests
=
Test Driven Development
Introduction
4
5. Méthodes traditionnelles : waterfall
5
User
requirements
Functional
specifications
Global technical
design
Detailed
technical design
Programming
Testing
CODE DRIVEN DEVELOPMENT
6. Méthodes traditionnelles : cycle en V
6
User
requirements
Functional
specifications
Global
technical design
Detailed
technical design
Programming
Component
tests
Integration
tests
System tests
Acceptance
tests
7. ❑ Lorsque les tests ne sont pas automatisés, éventuellement seuls les tests que l’on
pense être nécessaires seront utilisés
❑ Parfois seulement des tests fonctionnels sont utilisés (surtout en waterfall), rendant
souvent impossible le test des gestions d’erreurs
❑ Les tests unitaires (si existants) sont ajoutés après la phase de codage
❑ Couverture de code non complète
❑ Tout le code n’est pas testable (couplage fort entre interfaces)
❑ Tendre vers 100% de couverture demande un effort exponentiel
❑ On ne connaît pas la maturité du code avant les phases de test (et les résultats
peuvent être trompeurs)
Méthodes traditionnelles & tests
7
8. Méthodes traditionnelles & tests
8
❑ La détection des défauts intervient bien après la phase de codage
❑ Tous les défauts ne peuvent pas être découverts
❑ Un bug simple peut n’être découvert que très tard
❑ Plus un bug est trouvé tard, plus il est coûteux à fixer
❑ La qualité d’un logiciel ne se mesure pas seulement dans son comportement ni
dans ses performances
❑ Le code doit être maintenable, donc simple à comprendre
❑ Conséquences :
❑ Aucune ou peu de confiance dans la maturité du code
❑ Le projet prend du retard
❑ Les coûts ne sont pas maîtrisés
10. Exemple de bug couteux
10
❑ Ariane 5 – Juin 1996
o Un bug du logiciel de la fusée provoqua son autodestruction :
o Récupération du soft Ariane 4 : accélération codée sur 8 bit
o Sur Ariane 5 (accélération plus grande) => dépassement de capacité (9
bits auraient été nécessaires)
o Les pertes sont estimées à un total de 370 millions de dollars.
o http://fr.wikipedia.org/wiki/Vol_501_d%27Ariane_5
❑ Therac-25
o Machine de radiothérapie, évolution des modèles précédents
o Entre 1985 et 1987, des patients reçurent des doses massives de radiation,
au moins cinq patients décédèrent.
o Plusieurs problèmes de gestion du projet informatique furent découverts
après enquête, dont des tests logiciels incomplets.
o http://fr.wikipedia.org/wiki/Therac-25
11. Tests unitaires : pas suffisant !
11
Code testé :
int checksum(void *p, int len)
{
int sum = 0;
unsigned char* pp = (unsigned char*)p;
int i;
for (i = 0; i <= len; i++)
{
sum += *pp++;
}
return sum;
}
❑ Test unitaire :
char *myString = "foo";
assert(324 == checksum(myString, strlen(myString)));
❑ Couverture incomplète : bug potentiel non détecté !
Le problème avec cette fonction checksum
est dans la boucle for. La condition i <=
len doit être corrigée pour i < len, car
sinon, la boucle parcourra un octet de plus
que la longueur réelle de la chaîne, ce qui
entraînera un dépassement de mémoire
tampon et un comportement indéfini. De plus,
vous devriez utiliser un type de donnée plus
grand pour stocker la somme, comme
unsigned int, pour éviter un débordement
de la somme en cas de longues chaînes.
12. ❑ Code testé :
void checkRange(int i, int j)
{
if (i >= 3) throw std::exception("out of range");
if (j >= 3) throw std::exception("out of range");
}
❑ Test unitaire : 100% de couverture
EXPECT_THROW(checkRange(0, 3))
EXPECT_THROW(checkRange(3, 0))
❑ Mais supposons qu’un nombre négatif ne
soit pas admis :
checkRange(-1, 2);
Tests unitaires : pas suffisant !
12
Le problème principal avec cette fonction est qu'elle utilise
std::exception de manière incorrecte. std::exception est
une classe de base pour toutes les exceptions standard C++,
mais elle n'a pas de constructeur qui prend une chaîne de
caractères comme argument.
Pour créer une exception personnalisée avec un message, vous
devez définir votre propre classe d'exception dérivée de
std::exception et fournir un constructeur prenant une chaîne
de caractères comme argument.
EXPECT_THROW(checkRange(0, 3),
std::out_of_range);
EXPECT_THROW(checkRange(3, 0),
std::out_of_range);
try {
checkRange(-1, 2);
} catch (const std::out_of_range& e) {
// Gérer l'exception ici
}
Cette correction permet à la fonction checkRange de lancer une
exception std::out_of_range avec un message spécifique
lorsque les valeurs i ou j sont en dehors de la plage spécifiée.
13. ❑ TDD est une méthode de développement incrémental
❑ Le principe :
o En testant en premier, je conçois le code
❑ La méthode :
o Aucun code n’est implémenté avant d’avoir écrit un test unitaire qui échoue
o Ensuite, le code est implémenté, le test unitaire réussi
❑ Origine :
o eXtreme Programming (XP)
o 1999 Kent Beck, Martin Fowler et autres…
❑ Souvent utilisé dans un cycle Agile, mais ce n’est pas obligatoire !
❑ Très orienté DEVSECOPS avec des cycles CI/CD !
TDD
13
15. ❑ TDD inverse la méthode classique de codage en premier et debug plus tard
❑ TDD implique que le développeur d’un module se concentre donc dès le début sur :
o Son interface : comment vais-je l’utiliser ?
o Son comportement : que fait-il ?
o Sa réutilisation : clients multiples du code et des tests
o Ses dépendances : il doit être testé de façon isolée
o Sa cohésion : un module testable a une raison d’être
❑ Les détails d’implémentation sont secondaires
❑ Ceci encourage le découplage des interfaces
o Code plus modulaire
o Un système découplé est plus évolutif
❑ Chaque ajout de code aura un test ou des tests correspondant
Commencer par écrire des tests ?
15
Tests unitaires pour tester :
- ses classes
- ses API privées
- ses API publiques
16. ❑ L’automatisation des tests est un élément clé de TDD
o Continuous Integration (aka CI)
❑ Les tests sont simples
❑ A chaque étape du développement, de nouveaux tests sont ajoutés, suivi de
l’implémentation
❑ A chaque changement introduit dans du code existant, les tests sont exécutés
o Test du nouveau code
o Test du code existant
❑ Toute régression est ainsi immédiatement trouvée et est facile à corriger
Automatisation
16
17. Coûts des défauts
17
Bug introduit Bug découvert Cause trouvée Correction
Bug introduit
Bug découvert
Cause trouvée
Correction
❑ Traditionnellement :
❑ Avec TDD :
Parallélisation, réduction des temps d’action !
18. ❑ Meilleure couverture de test (but : 100%)
❑ Moins de défauts, moins d’effets de bord, moins de temps passé à debugger
❑ Le code est conçu de façon à être plus facile à tester
❑ Code plus modulaire
❑ Design plus propre et plus facile à comprendre
❑ La refactorisation devient plus facile
❑ Les tests constituent une documentation bas-niveau
o Pour chaque feature, il y a au moins un exemple d’utilisation
o Toujours à jour !
TDD : avantages
18
19. ❑ TDD ne garantie pas une bonne architecture ou design
❑ TDD est plus difficile à utiliser dans les situations où des tests fonctionnels sont
requis pour déterminer le succès ou l'échec
o Interfaces homme-machine
o Base de données
o Réseaux
❑ Le support du management est essentiel
o Sans l’entière organisation convaincue que TDD va améliorer le produit, le temps
passé à écrire les tests est souvent vu comme perdu
❑ Les tests sont typiquement écrits par le développeur du code testé
❑ TDD ne remplace pas les autres activités de test (intégration, validation,
conformité, etc)
TDD : vulnérabilités
19
20. ❑ Bob Martin décrit TDD avec trois règles simples :
o Do not write production code unless it is to make a failing unit test pass ;
o Do not write more of a unit test than is sufficient to fail, and build failures are
failures ;
o Do not write more production code than is sufficient to pass the one failing unit
test.
http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
TDD : les trois lois
20
22. ❑ Les tests montrent que le comportement est correct : le code fonctionne
❑ Mais du code doit être propre et bien structuré
o Plus facile à comprendre
o Plus facile à faire évoluer
o Plus facile à maintenir
❑ C’est le but de la dernière étape du microcycle
❑ La refactorisation est l’activité de changer la structure d’un code sans en
changer son comportement
❑ Souvent appelé « Red-Green-Refactor »
TDD : refactorisation
22
25. TDD : exemple
25
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
return 0;
}
⮚Code :
• Le test ne compile pas car le code est inexistant !
26. TDD : exemple
26
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
return 0;
}
⮚Code :
class Quad {
public:
Quad () {;}
int area(int w, int h) { return 6; }
};
→ Cette fonction a pour but de calculer
l’aire d’un quadilatère.
• Le test compile et réussi !
27. TDD : exemple
27
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
return 0;
}
⮚Code :
class Quad {
public:
Quad () {;}
int area(int w, int h) { return w * h; }
};
• Le test passe toujours
28. TDD : exemple
28
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
assert(10 == q.perimeter(2, 3));
return 0;
}
⮚Code :
class Quad {
public:
Quad () {;}
int area(int w, int h) { return w * h; }
};
• Le test ne compile plus !
29. TDD : exemple
29
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
assert(10 == q.perimeter(2, 3));
return 0;
}
⮚Code :
class Quad {
public:
Quad () {;}
int area(int w, int h) { return w * h; }
int perimeter(int wd, int ht)
{ return 10; }
};
• Le test passe de nouveau !
30. TDD : exemple
30
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
assert(10 == q.perimeter(2, 3));
return 0;
}
⮚Code :
class Quad {
public:
Quad () {;}
int area(int w, int h) { return w * h; }
int perimeter(int wd, int ht)
{ return wd + wd + ht + ht; }
};
• Le test passe toujours !
31. TDD : exemple
31
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q;
assert(6 == q.area(2, 3));
assert(10 == q.perimeter(2, 3));
return 0;
}
⮚Code :
class Quad {
public:
Quad (int wd, int ht) :
m_wd(wd),
m_ht(ht)
{;}
int area() { return m_wd * m_ht; }
int perimeter()
{ return m_wd + m_wd + m_ht + m_ht; }
private:
int m_wd;
int m_ht;
};
• Le code est refactorisé, mais le test ne compile plus !
• Sachant que le test ne passerait plus, il aurait pu être modifié en
premier
32. TDD : exemple
32
⮚Test :
#include <assert.h>
#include "quad.h"
int main()
{
Quad q(2, 3);
assert(6 == q.area());
assert(10 == q.perimeter());
return 0;
}
⮚Code :
class Quad {
public:
Quad (int wd, int ht) :
m_wd(wd),
m_ht(ht)
{;}
int area() { return m_wd * m_ht; }
int perimeter()
{ return m_wd + m_wd + m_ht + m_ht; }
private:
int m_wd;
int m_ht;
};
• Le test compile de nouveau et passe
33. ❑ Cet exemple est loin d’être parfait et reste incomplet…
o En effet, la classe Quad accepte des valeurs négatives
o Faut-il ajouter des tests avec valeurs négatives ?
o Dans ce cas, elles doivent être testées dans le constructeur
o Faut-il modifier la classe avec des entiers non signés ?
❑ Si les tests réussissent, cela ne signifie pas qu’il n’y a pas de bug !
❑ En règle générale, le développeur doit toujours tester les valeurs extrêmes passées
en argument, les pointeurs nuls, les débordements de variables…
❑ La relecture de code par des pairs devrait donc aussi s’attarder sur les tests
❑ Attention à la refactorisation qui peut induire que le code n’est plus couvert par
les tests à 100%
TDD : exemple
33
34. ❑ Un framework de tests unitaires fournit :
o Un langage commun pour exprimer les cas de test (eg: google test)
o Un langage commun pour exprimer les résultats attendus
o Permet d’accéder aux features du langage du code testé
o Collecte les résultats de test, fournit un rapport de test
o Fournit un mécanisme pour faire tourner les tests
o Soit tous les tests
o Soit seulement certains tests
❑ Quatre phases pour chaque test :
1. Etablissement des pré-conditions du test (passant/nominal et générant des erreurs)
2. Exercice du code testé
3. Vérification des résultats
4. Retour du système testé à son état initial
❑ http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
Test frameworks
34
35. ❑ Les tests unitaires doivent être confinés à un processus
❑ Il est déconseillé d’avoir des tests unitaires faisant appel à des systèmes externes (réseau,
base de données, hardware, …)
o Ne pas confondre tests unitaires et tests d’intégration
o Dépendances externes pouvant influer sur les tests
o Ralentit l’exécution des tests unitaires
❑ Lorsque du code testé unitairement dépend d’une ressource externe, une interface doit
représenter cette ressource
❑ L’implémentation de cette interface doit être faite de deux façons :
o Appels réels
o Appels simulés ou stubbé/mock (Eg: google moc)
❑ Le code testé par tests unitaires appellera l’implémentation simulée/stubé/mocké
Injection de dépendances
35
36. ❑ Fakes :
o Fonctions ou objets qui ne font pas grand-chose, mais qui permettent à un test de
vérifier un comportement correct
o Retour d’une valeur prédéterminée
❑ Mocks :
o Fonctions ou objets qui sont plus évolués que des fakes
o Peuvent contenir eux-mêmes des assertions
o Peuvent simuler un comportement
❑ Un avantage indéniable des fakes et mocks est la possibilité de simuler des
conditions d’erreur, ce qui est parfois impossibles à réaliser avec l’implémentation
réelle
❑ Il reste possible de faire tourner une sélection des tests unitaires contre
l’implémentation réelle (= tests d’intégration)
Fakes & mocks
36
37. ❑ Le test unitaire de fonctions statiques ou de méthodes privées reste un débat non
tranché
❑ Le problème est de permettre aux tests unitaires d’appeler ces fonctions :
o Différenciation des interfaces par #ifdef ou autres équivalents
o N’aide pas à garder un code propre
o Ajout de fonctions que les tests appelleront
o Ce peut être automatiquement fait par le préprocesseur
o Par pointeurs sur fonctions
o Certains langages offrent par « réflexion » un moyen de test
Fonctions statiques & méthodes privées
37
38. ❑ Pratique de TDD de 2003 à 2009 en C et C++ (embedded software)
o Dernier projet : 45Ksloc, ~600 tests, ~45 secondes
❑ TDD est contre intuitif au premier abord
o Ecrire les tests en premier
❑ Maîtriser TDD prend du temps
o Découplage des interfaces
o Eviter les dépendances entre tests
❑ TDD allié à d’autres techniques permet d’atteindre un niveau de maturité
(mesurable) impossible à atteindre autrement
o Stratégie globale de codage et de test
o Tests d’intégration et fonctionnels automatisés
o Une régression doit devenir la priorité numéro 1
o Si défaut trouvé en aval, rajouter un test en amont
Expérience personnelle
38
39. ❑ Qualité du code de test doit être au même niveau que le code testé
o Aucun warning autorisé (au minimum -Wall –Wextra)
o Facilite la maintenance du code de test
❑ Appliquer TDD à du code existant est difficile voire parfois impossible
❑ TDD ne peut pas sauver un projet si les spécifications ne sont pas correctes
Expérience personnelle
39
41. Frameworks de tests unitaires
41
❑ Il existe une multitude de framework par langage :
o C : xTests, CUnit, Google/test, API Sanity autotest, CU, …
o C++ : Google/test, CppUnit, CppTest, Tpunit++, …
o Java : Junit, jWalk, …
o PHP : PHPUnit, line, jMock
o Python : PyUnit / xPyUnit / TestOOB, Nose
o ObjectiveC : Iphone unit testing, ObjcUnit, GHUnit, OCUnit
o Ruby : RSPec, Test::Unit, minitest
o Shell : assert.sh, shUnit/shUnit2, ATF, filterunit
o XML : Xunit, WUnit
Pour plus d’infos : http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
42. Système d’intégration continue
42
❑ L'intégration continue est un ensemble de pratiques utilisées en génie logiciel. Elles consistent à
vérifier à chaque modification de code source que le résultat des modifications ne produit pas de
régression de l'application en cours de développement. Bien que le concept existait auparavant,
l'intégration continue se réfère généralement à la pratique de l'extreme programming.
❑ Il existe une multitude de logiciels d’intégration continue :
o Apache Continuum
o Bamboo
o Buildbot
o CruiseControl
o Jenkins/Hudson
o Gitlab
Pour plus d’information : http://en.wikipedia.org/wiki/Comparison_of_continuous_integration_software
43. ❑ Le fuzzing est une technique pour tester des logiciels. L'idée est d'injecter des données aléatoires dans les entrées
d'un programme. Si le programme échoue (par exemple en crashant ou en générant une erreur), alors il y a des
défauts à corriger. Exemples de point d'entrée d'un programme :
o Fichiers ;
o Périphériques (clavier, souris, …) ;
o Variable d’environement
o Réseau
o Limitation de ressources (CPU, Mémoire, Accès I/O, … ) ;
o Etc …
Fuzzing
43
44. ❑ Le grand avantage du fuzzing est que l'écriture de tests est extrêmement simple, ne demande aucune connaissance du
fonctionnement du système et permet de trouver des vulnérabilités facilement. D'ailleurs, le fuzzing est également utilisé
pour traquer des failles de sécurité ou dans la rétro-ingénierie.
❑ La première trace du fuzzing est la publication datant du 12 décembre 1990 : « An Empirical Study of the Reliability of UNIX
Utilities » [1] écrite par Barton P. Miller, Lars Fredriksen, et Bryan So. Le résumé indique que durant les essais ils ont été
capables de crasher 25 à 33% des programmes utilitaires de n'importe quelle version d'UNIX ». Le rapport présente les outils
de test mais également l'origine des erreurs.
❑ Le fuzzing est tellement simple à utiliser et efficace pour trouver des vulnérabilités que le chercheur en sécurité
informatique Charlie Miller a refusé de dévoiler les vulnérabilités zero day trouvées dans le code de logiciels célèbres
(contrairement au règlement du concours de sécurité informatique Pwn2own), afin de protester contre les éditeurs qui
n'utilisent pas assez cette technique simple selon lui.
❑ Inversement au fuzzing qui est une méthode de test par boîte noire, la méthode de test par boîte blanche analyse un
système dont on connaît exactement le fonctionnement.
fuzzing
44
46. ❑ 🡪 Google/test : framework multiplateforme permettant l’écriture de tests en C/C++. Basé sur
l’architecture xUnit il supporte la découverte de tests en automatique et offre une multitude de macro.
o Avantage(s):
o Facile à utiliser via un ensemble de macros
o Nombre de fonctionnalités (jouer un test x fois, random, … )
o Compatible xUnit
o Inconvénient(s):
o Mauvais support autotools
❑ 🡪 Google/mock :
o Avantage(s):
o Permet de tester des composants complexes
o Inconvénient(s):
o Peut être complexe à implémenter (doit être inclus dans les plannings)
o Risque de dérive par rapport au comportement réel
Google test / mock
46
52. TDD & C/C++/google test
Développement d’un composant suivant une démarche TDD
52
foo.h/hpp
API (H/HPP) Framework
Google/test
Composant compilé par
gcc/g++ :
- binaire sans le main()
- Librairie statique/dynamique
Test unitaire gtest_foo.cpp
testant l’API de foo.h
Test unitaire
Librairie
gtest.so
LINK par LD
Exigences
API
API
gtest.h libgests.s
o
API
CODE
foo.o
gtest_foo.o
Runtime: ./gtest_foo
Résultat des tests au format Junit/XML
53. 53
TDD & C/C++/google test
Développement d’un composant suivant une démarche TDD
SUT
MOCK 1
MOCK 2
(STUB)
Composant en
développement
(C/C++)
API
(H/HPP)
Test unitaire
LINK par LD
framework
Google/test
Programme
de tests basé sur
l’API
Résultat des tests au format
Junit/XML
pilotage pilotage
Composant
Réel
(Ex: bdd)
54. TDD / C++ / JENKINS
54
Jenkins
Repository
GIT,
Clearcase,
Subversion,
…
Développeur
d’un
composant
trigger
Build du composant: make
Configuration du composant:
./configure –prefix=/usr
Installation dans le stagingdir:
make DESTDIR=<stagingdir>
install
Installation dans le rootfs final:
make DESTDIR=<rootfsdir> install
Lancement des tests unitaires:
make check
Mesure de couverture de code:
gcov/lcov
Génération de la doc au format
doxygen: make html
56. ❑ Contexte : Création d’une application Web de gestion
❑ Technologies Projet : J2EE, JSF, Hibernate, Spring
❑ Mode de contractualisation : Forfait Agile/SCRUM
❑ Sprints (itérations) de 4 semaines
❑ Mise en œuvre de pratiques d’eXtreme Programming :
o Test Driven Development : développement orienté par les tests
o Revue de pair
o Intégration Continue
❑ Solution technique
o Tests unitaires sur les couches métiers : JUnit
o Serveur d’intégration continue : Hudson
o Plugins :
o JUnit : non régression
o Duplicate Code : code dupliqué et factorisation possible
o CheckStyle : respect des règles de développement
o PMD : qualité du code
o Cobertura : couverture du code par les tests unitaires
o Java NCSS : commentaires & documentation
REX Viaccess : contexte
56
58. ❑ Itérations courtes : JUnit permet de se protéger contre la régression
et évite de retester exhaustivement l’application à chaque itération
❑ Avantages :
o passage automatique :
o à chaque modification dans SVN
o Régulièrement (toutes les 2 heures ou build de nuit)
o notification des résultats part mail
❑ Inconvénients : se limite aux tests unitaires
❑ développés
REX Viaccess : JUnit
58
59. ❑ Duplicate Code : code dupliqué et factorisation possible
❑ Avantages : reconnait les des bouts de code copiés/collés mais
également des structures de données approchantes => conception
objet à améliorer
❑ Inconvénients : ne prend pas en compte le métier
REX Viaccess : Duplicate Code
59
60. ❑ Respect des règles de développement
❑ Avantages :
o paramétrable suivant les règles de développement
du client
o Apporte une explication détaillée sur comment
corrigé le problème
❑ Inconvénients : aucun
REX Viaccess : CheckStyle
60
61. ❑ PMD : qualité du code
❑ Avantages :
❑ Inconvénients :
REX Viaccess : PMD
61
62. ❑ Cobertura permet de vérifier la couverture du code par les tests
unitaires
❑ Avantages :
❑ Inconvénients :
REX Viaccess : Cobertura
62
63. ❑ Java NCSS : mensure du taux de commentaires et de documentation
❑ Avantage : donne une indication de l’état du projet
❑ Inconvénient : résultats pas exploitable pour action
REX Viaccess : Java NCSS
63