SlideShare une entreprise Scribd logo
1  sur  71
Tests Driven Developments (aka TDD)
Thierry GAYET - 01/2024
❑ 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
Introduction
❑ 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
Méthodes traditionnelles : waterfall
5
User
requirements
Functional
specifications
Global technical
design
Detailed
technical design
Programming
Testing
CODE DRIVEN DEVELOPMENT
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
❑ 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
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
Méthodes traditionnelles : coût des défauts
9
Source : Software Engineering Economics, B.W. Boehm, 1981
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
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.
❑ 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.
❑ 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
TDD
14
❑ 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
❑ 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
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 !
❑ 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
❑ 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
❑ 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
TDD : microcycle
21
❑ 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
TDD : microcycle
23
Exemple avec une classe en C++
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 !
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 !
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
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 !
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 !
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 !
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
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
❑ 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
❑ 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
❑ 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
❑ 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
❑ 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
❑ 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
❑ 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
Test unitaire / intégration continue
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
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
❑ 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
❑ 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
Retours d’expériences :
[ google test, google mock, jenkins, c++ ]
❑ 🡪 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
Libsrt-nn6
47
Repo git(lab) : https://gitlab.enensys.com/common/sw/standards/libsrt-
nn6
API / header : https://gitlab.enensys.com/common/sw/standards/libsrt-
nn6/-/blob/develop/include/srt-nn6.h?ref_type=heads
Implementation :
https://gitlab.enensys.com/common/sw/standards/libsrt-nn6/-
/blob/develop/src/srt-nn6.cpp?ref_type=heads
Code C++17 google/test :
https://gitlab.enensys.com/common/sw/standards/libsrt-nn6/-
/blob/develop/test/srt-nn6-unittest-library.cpp?ref_type=heads
Libsrt-nn6 & c++/google/test
48
Libsrt-nn6 & c++/google/test
49
50
● Documentation / exemples :
○ https://github.com/google/googletest/tree/main/googletest/samples
○ https://github.com/google/googletest/blob/main/googletest/samples/sa
mple1.cc
○ https://github.com/google/googletest/blob/main/googletest/samples/sa
mple1.h
Libsrt-nn6 & c++/google/test
51
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
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)
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
Retours d’expériences :
[ JSEE, JSF, Hibernate, Spring ]
❑ 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
REX Viaccess : Architecture de l’usine
développement
57
❑ 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
❑ 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
❑ 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
❑ PMD : qualité du code
❑ Avantages :
❑ Inconvénients :
REX Viaccess : PMD
61
❑ Cobertura permet de vérifier la couverture du code par les tests
unitaires
❑ Avantages :
❑ Inconvénients :
REX Viaccess : Cobertura
62
❑ 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
DEVSECOPS & CI/CD
CI : Continuous Integration
CD : Continuous Deployment
DEVSECOPS
65
DEVSECOPS
66
IAC : Infrastructure As Code
IAST : Interactive Application Security Testing
RASP : Runtime Application Self-Protection
SCA : Software Composition Analysis
SAST : Static Application Security Testing
DAST : Dynamic Application Security Testing
WAF : Web Application Firewall
WAAS : Web Application Acceleration Service
DEVSECOPS
67
DEVSECOPS
68
DEVSECOPS
69
DEVSECOPS
70
Questions ?
71

Contenu connexe

Similaire à Test Driven Development (aka TDD) for agile teams

Commencer avec le tdd
Commencer avec le tddCommencer avec le tdd
Commencer avec le tddEric Hogue
 
TDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoringTDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoringneuros
 
Pratiques de développement pour équipes Agile
Pratiques de développement pour équipes AgilePratiques de développement pour équipes Agile
Pratiques de développement pour équipes AgileAgile Tour 2009 Québec
 
[Agile Testing Day] Test Driven Development (TDD)
[Agile Testing Day] Test Driven Development (TDD)[Agile Testing Day] Test Driven Development (TDD)
[Agile Testing Day] Test Driven Development (TDD)Cellenza
 
Human Talks Grenoble - 11/12/2012 - TDD
Human Talks Grenoble - 11/12/2012 - TDDHuman Talks Grenoble - 11/12/2012 - TDD
Human Talks Grenoble - 11/12/2012 - TDDXavier NOPRE
 
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testable
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testableAgile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testable
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testableAssociation Agile Nantes
 
Une architecture agile et testable
Une architecture agile et testableUne architecture agile et testable
Une architecture agile et testablemartinsson
 
Automatisation des tests - objectifs et concepts - partie 1
Automatisation des tests  - objectifs et concepts - partie 1Automatisation des tests  - objectifs et concepts - partie 1
Automatisation des tests - objectifs et concepts - partie 1Christophe Rochefolle
 
C'est quoi, du bon code ?
C'est quoi, du bon code ?C'est quoi, du bon code ?
C'est quoi, du bon code ?Rémi Lesieur
 
20131024 qualité de code et sonar - mug lyon
20131024   qualité de code et sonar - mug lyon20131024   qualité de code et sonar - mug lyon
20131024 qualité de code et sonar - mug lyonClement Bouillier
 
Tester c'est douter - Linkvalue tech
Tester c'est douter - Linkvalue techTester c'est douter - Linkvalue tech
Tester c'est douter - Linkvalue techMarine Karam
 
Développement en méthode agile
Développement en méthode agileDéveloppement en méthode agile
Développement en méthode agilelaurent bristiel
 
Le rôle du testeur et le Blackbox testing
Le rôle du testeur et le Blackbox testingLe rôle du testeur et le Blackbox testing
Le rôle du testeur et le Blackbox testingGeeks Anonymes
 
Test de logiciels
Test de logiciels Test de logiciels
Test de logiciels Bilel Abed
 
Université de la performance
Université de la performanceUniversité de la performance
Université de la performancepkernevez
 
AgileTour Toulouse 2012 : clean code en pratique
AgileTour Toulouse 2012 : clean code en pratiqueAgileTour Toulouse 2012 : clean code en pratique
AgileTour Toulouse 2012 : clean code en pratiqueAgile Toulouse
 

Similaire à Test Driven Development (aka TDD) for agile teams (20)

Commencer avec le tdd
Commencer avec le tddCommencer avec le tdd
Commencer avec le tdd
 
TDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoringTDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoring
 
Agile Methodologies
Agile MethodologiesAgile Methodologies
Agile Methodologies
 
Pratiques de développement pour équipes Agile
Pratiques de développement pour équipes AgilePratiques de développement pour équipes Agile
Pratiques de développement pour équipes Agile
 
[Agile Testing Day] Test Driven Development (TDD)
[Agile Testing Day] Test Driven Development (TDD)[Agile Testing Day] Test Driven Development (TDD)
[Agile Testing Day] Test Driven Development (TDD)
 
Human Talks Grenoble - 11/12/2012 - TDD
Human Talks Grenoble - 11/12/2012 - TDDHuman Talks Grenoble - 11/12/2012 - TDD
Human Talks Grenoble - 11/12/2012 - TDD
 
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testable
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testableAgile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testable
Agile Tour Nantes 2014 - Tdd, le meilleur moyen d'écrire du code testable
 
Une architecture agile et testable
Une architecture agile et testableUne architecture agile et testable
Une architecture agile et testable
 
Automatisation des tests - objectifs et concepts - partie 1
Automatisation des tests  - objectifs et concepts - partie 1Automatisation des tests  - objectifs et concepts - partie 1
Automatisation des tests - objectifs et concepts - partie 1
 
C'est quoi, du bon code ?
C'est quoi, du bon code ?C'est quoi, du bon code ?
C'est quoi, du bon code ?
 
20131024 qualité de code et sonar - mug lyon
20131024   qualité de code et sonar - mug lyon20131024   qualité de code et sonar - mug lyon
20131024 qualité de code et sonar - mug lyon
 
Tester c'est douter - Linkvalue tech
Tester c'est douter - Linkvalue techTester c'est douter - Linkvalue tech
Tester c'est douter - Linkvalue tech
 
Développement en méthode agile
Développement en méthode agileDéveloppement en méthode agile
Développement en méthode agile
 
Le rôle du testeur et le Blackbox testing
Le rôle du testeur et le Blackbox testingLe rôle du testeur et le Blackbox testing
Le rôle du testeur et le Blackbox testing
 
Flex Unit Testing
Flex Unit TestingFlex Unit Testing
Flex Unit Testing
 
Test de logiciels
Test de logiciels Test de logiciels
Test de logiciels
 
chap6_GL.pptx
chap6_GL.pptxchap6_GL.pptx
chap6_GL.pptx
 
Université de la performance
Université de la performanceUniversité de la performance
Université de la performance
 
Clean code en pratique
Clean code en pratiqueClean code en pratique
Clean code en pratique
 
AgileTour Toulouse 2012 : clean code en pratique
AgileTour Toulouse 2012 : clean code en pratiqueAgileTour Toulouse 2012 : clean code en pratique
AgileTour Toulouse 2012 : clean code en pratique
 

Test Driven Development (aka TDD) for agile teams

  • 1. Tests Driven Developments (aka TDD) Thierry GAYET - 01/2024
  • 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
  • 9. Méthodes traditionnelles : coût des défauts 9 Source : Software Engineering Economics, B.W. Boehm, 1981
  • 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
  • 24. Exemple avec une classe en C++
  • 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
  • 40. Test unitaire / intégration continue
  • 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
  • 45. Retours d’expériences : [ google test, google mock, jenkins, c++ ]
  • 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
  • 47. Libsrt-nn6 47 Repo git(lab) : https://gitlab.enensys.com/common/sw/standards/libsrt- nn6 API / header : https://gitlab.enensys.com/common/sw/standards/libsrt- nn6/-/blob/develop/include/srt-nn6.h?ref_type=heads Implementation : https://gitlab.enensys.com/common/sw/standards/libsrt-nn6/- /blob/develop/src/srt-nn6.cpp?ref_type=heads Code C++17 google/test : https://gitlab.enensys.com/common/sw/standards/libsrt-nn6/- /blob/develop/test/srt-nn6-unittest-library.cpp?ref_type=heads
  • 50. 50
  • 51. ● Documentation / exemples : ○ https://github.com/google/googletest/tree/main/googletest/samples ○ https://github.com/google/googletest/blob/main/googletest/samples/sa mple1.cc ○ https://github.com/google/googletest/blob/main/googletest/samples/sa mple1.h Libsrt-nn6 & c++/google/test 51
  • 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
  • 55. Retours d’expériences : [ JSEE, JSF, Hibernate, Spring ]
  • 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
  • 57. REX Viaccess : Architecture de l’usine développement 57
  • 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
  • 64. DEVSECOPS & CI/CD CI : Continuous Integration CD : Continuous Deployment
  • 66. DEVSECOPS 66 IAC : Infrastructure As Code IAST : Interactive Application Security Testing RASP : Runtime Application Self-Protection SCA : Software Composition Analysis SAST : Static Application Security Testing DAST : Dynamic Application Security Testing WAF : Web Application Firewall WAAS : Web Application Acceleration Service