Débogage ([wikipedia w])
-
Un bogue (bug) est un défaut dans un programme qui l’empêche de fonctionner correctement
-
Le débogage (debugging) est une activité ayant pour objectif de localiser les bogues dans un programme
-
Le débogage est basé sur la confirmation
-
c’est un processus destiné à confirmer les choses que l’on croit vrai jusqu’à en trouver une qui ne l’est pas
-
-
Un débogueur (debugger) est un outil fournissant une aide pour le débogage
-
Pour gagner du temps !
-
Les alternatives sont moins efficaces (mais parfois utiles tout de même)
-
utiliser des affichages (
printf, …)-
perte de temps
-
beaucoup d’éditions/compilations pour ajouter/enlever les affichages
-
moins informatif
-
-
utiliser une bibliothèque de journalisation (logging) ([wikipedia w])
-
plus pratique que les affichages
-
moins informatif que le débogueur
-
-
-
Exécution contrôlée du programme
-
pas à pas sommaire (sans entrer dans les fonctions)
-
pas à pas détaillé
-
retour en arrière (plus rare)
-
-
Pose de points d’arrêt (breakpoints)
-
repère sur une instruction signalant au débogueur qu’il doit faire un pause dans l’exécution lorsqu’il arrive à cette instruction
-
peut être également associé à une condition
-
un point d’observation (watchpoint) stoppe le programme lorsque l’état d’une expression change
-
un catchpoint fait de même quand un événement se déclenche
-
-
Visualisation de l’état du programme
-
variables, pile d’appel, …
-
certains débogueurs permettent l’affichage de structure de données complexes
-
-
Modification de l’état du programme
-
Débogage à distance
-
Tenter de reproduire le bogue
-
Simplifier les entrées du programme
-
Exécuter le programme sous le contrôle du débogueur
-
Se positionner à l’endroit de l’erreur signalée ou au milieu du programme (pose d’un breakpoint) si aucune erreur n’est signalée
-
Examiner le contexte pour confirmer que les variables possèdent les valeurs attendues
-
Déterminer la position suivante à étudier et retourner en 5
|
Important
|
nécessite de compiler le programme avec les informations de débogage (option -g de javac ou de gcc)
|
Tests ([wikipedia w])
-
Les tests visent à mettre en évidence des défauts de l’élément testé
-
L’objectif final est bien sûr de réduire le nombre de bogues présents dans un programme
-
Un test est un ensemble de cas à tester (conditions initiales, entrées, observations attendues)
|
Warning
|
Un test ne permet pas de prouver l’absence de bogue (≠ des méthodes formelles).
|
-
Un test boite blanche (white box) s’appuie sur une connaissance de la structure interne de l’élément à tester
-
Un test boite noire (black box) s’appuie sur les spécifications de l’élément
-
Un test de non régression vérifie que le système ne se dégrade pas lors de ses évolutions
-
Un test fonctionnel s’assure que les résultats attendus sont bien obtenus
-
Un test non fonctionnel analyse les propriétés non fonctionnelles d’un système
-
test des performances pour vérifier l’efficacité du système
-
test de sécurité pour s’assurer du respect des règles de confidentialité
-
| Unitaire |
Les tests unitaires vérifient la conformité des éléments de base d’un programme (fonctions, classes, …) et sont en général réalisés par le développeur. |
| Intégration |
Les tests d’intégration vérifient la cohérence des différents modules et la bonne communication entre eux. |
| Système |
Les tests systèmes concernent l’ensemble du projet et son intégration dans son environnement. |
| Recette |
Les tests de recette (ou d’acceptation) confirment la conformité du système avec les besoins. |
-
Généralement (cycle de développement en V par exemple), les tests sont réalisés par un groupe de testeurs après la réalisation des fonctionnalités
-
Une pratique encouragée par les méthodes Agiles et XP consiste à débuter le processus par les tests (Développement dirigé par les tests)
| Unitaires et d’intégration |
cf. ci-dessous. |
| Fonctionnel ([wikipedia w]) | |
| Non fonctionnels ([wikipedia w]) |
-
Un test unitaire (unit test) vise à augmenter la confiance du programmeur dans des portions du code source
-
Une unité fait référence à la plus petite partie testable de l’application (fonction, méthode)
-
Le but des tests unitaires est d’isoler chaque partie du programme pour la tester indépendamment
-
isoler les différents éléments nécessite souvent d’avoir recours à du code de substitution (stub, fake ou mock object)
-
-
Ces tests peuvent être réalisés à la main (affichages et vérification visuel, débogueur) ou avec un framework spécialisé par exemple de type
xUnit
| Java | Python | Bash |
|---|---|---|
-
Pour chaque unité, on écrit une ou plusieurs méthodes de test
-
un outil de gestion est nécessaire vu le nombre de tests
-
-
Une possibilité intéressante est d’écrire le test avant la méthode
-
précise d’abord ce que doit faire la méthode
-
-
L’ensemble des tests peut ensuite être répété autant que nécessaire
-
l’exécution des tests après chaque modification permet de vérifier la non régression
-
-
Petits (analyse d’un point précis) et rapides (exécutés souvent)
-
Totalement automatisés
-
Toujours au niveau de l’unité
-
Indépendants les uns des autres (pas de contraintes d’ordre)
-
N’utilisent pas de ressources externes (SGBD, …)
-
Un test unitaire se focalise sur un élément particulier
-
Ce dernier peut être dépendant d’autres éléments
-
Une doublure de test permet de remplacer ces dépendances
| Fantôme |
un objet fantôme (dummy) sert juste à remplir des listes de paramètres |
| Substitut |
un objet substitut (fake) fournit une implémentation simplifiée |
| Bouchon |
un objet bouchon (stub) retourne des réponses prédéfinies spécifiques aux tests |
| Simulacre |
un objet simulacre (mock) sont préprogrammés par des attentes, i.e. une spécification du comportement attendu |
| Java |
|---|
-
L’objectif est de vérifier que les tests unitaires couvrent bien l’ensemble du code écrit
-
La couverture de code (code coverage) est un outil de mesure de la qualité des tests effectués
-
Le degré de couverture est mesuré par des indices statistiques
-
Les portions de codes non testées sont mises en évidence
-
Le Statement Coverage (ou Line Coverage) mesure le degré d’exécution de chaque ligne
-
simple mais ignore un certain nombre d’erreurs simples (ne prend pas en compte la logique du programme)
-
-
Le Condition Coverage indique si toutes les conditions ont été évaluées
-
les conditions doivent être évaluées à vrai et à faux pour obtenir un taux de 100\%
-
aide à résoudre les problèmes de la mesure précédente
-
-
Le Path coverage examine si chaque chemin a été parcouru
-
Le Function Coverage vérifie si chaque fonction a été appelée
|
Important
|
Un score de 100% ne garantit pas la correction du programme. Ce n’est même pas un objectif ! |
| Java |
|---|
-
Le développement piloté par les tests (Test Driven Development ou TDD) est une méthode de développement mettant l’accent sur les tests unitaires
-
Cette méthode préconise d’écrire les tests avant le code
-
Only ever write code to fix a failing test
-
-
Cette approche permet de spécifier ce que l’on attend du système avant de le réaliser
-
Elle est basée sur les tests et le \href{http://www.refactoring.com/{refactoring}}
-
Le refactoring consiste à améliorer la conception du programme sans en changer le comportement (les fonctionnalités)
-
Le TDD n’est pas limité aux tests unitaires mais s’applique aussi aux tests de recette (Acceptance TDD)
-
Le TDD s’appuie sur de courtes itérations
-
Chaque itération possède cinq étapes
-
Écrire un test
-
Exécuter les tests et vérifier que le nouveau échoue
-
Écrire juste le code nécessaire pour faire passer le test
-
Réexécuter les tests et vérifier que tous les tests passent
-
Corriger la conception du système (refactoring)
-
-
La phase de refactoring s’applique aussi bien au code de l’application qu’au code des tests
Programmation pilotée par le comportement ([wikipedia w])
-
Les pratiques de TDD peuvent être étendues vers la Programmation pilotée par le comportement (Behaviour-Driven Development ou BDD)
Optimisation et profilage ([wikipedia w])
-
L'optimisation est la pratique qui consiste à modifier un système pour qu’il fonctionne plus efficacement
-
par exemple plus rapidement ou en consommant moins de ressources
-
L’optimisation est souvent un compromis entre différents facteurs
-
-
L'analyse dynamique (profiling) d’un programme a pour objectif de collecter des informations sur le comportement d’une application pendant son exécution
-
Les éléments à surveiller sont l’utilisation des CPU, l’utilisation de la mémoire, les threads, …
-
|
Warning
|
Ce type d’analyse a un impact sur le comportement de l’application. |
-
Un outil d’analyse dynamique permet de collecter et de présenter les informations résultant de l’analyse de l’exécution
-
Utilisé pour l’analyse de performances, un tel outil permet de localiser les points chauds (hot spots) du programme
-
point chaud: portion de code longue à exécuter
-
rapports sur les fonctions appelées, temps passé dans chaque fonction, …
-
|
Warning
|
La phase d’optimisation ne doit intervenir qu’une fois que le programme fonctionne et répond aux spécifications fonctionnelles. |
-
More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity., W.A. Wulf
-
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%., Donald Knuth
-
Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you have proven that’s where the bottleneck is., Rob Pike
-
The First Rule of Program Optimization: Don’t do it. The Second Rule of Program Optimization (for experts only!): Don’t do it yet., Michael A. Jackson
-
Conception
-
algorithmes, architecture de l’application, …
-
-
Code source
-
utilisation d’idiomes adaptés au langage WARNING: attention de ne pas perturber les optimisations du compilateur
-
-
Compilateur
-
utiliser les optimisations fournies par le compilateur
-
-
Assembleur
-
spécifique à une plateforme
-
-
Exécution
-
compilateur just in time
-
-
Choisir un paramètre à optimiser (temps CPU, occupation mémoire, …)
-
Localiser les portions de code les plus coûteuses vis à vis de ce paramètre
-
permet d’obtenir le meilleur rendement
-
règle des 80/20 ([wikipedia w])
-
-
Appliquer les optimisations puis mesurer le résultat