Vous êtes sur la page 1sur 99

INF3134 Outils pour le développement logiciel

3A – S6 – 2023-2024

Cours 1:

Contrôle des versions – Git, GitLab


Tests, Bugs, Tests unitaires

Gestion du build, intégration logicielle

Felicia IONASCU
Aylen RICCA
Sommaire
1. Contrôle des versions
2. Git, GitLab
3. Test, bugs, techniques de debug
4. Tests unitaires
5. Gestion du build, intégration logicielle

2. Architecture
1. Spécifications
et Conception

Génie Logiciel
5. Maintenance 3. Développement 2
4. Test
Sommaire
1. Contrôle des versions
2. Git, GitLab
3. Test, bugs, techniques de debug
4. Tests unitaires
5. Gestion du build, intégration logicielle

3
Contrôle des versions
3.Développement

• Problème 1:
• Très souvent plusieurs développeurs travaillent sur le même code source
• Comment s’assurer que le nouveau code n’est pas perdu?
• Comment s’assurer que ce qu’ils écrivent marche correctement quand exécuté sur une machine
tiers?

4
Contrôle des versions
3.Développement

• Problème 2:
• Un bug critique a été trouvé dans la première version d’une application qui se trouve sur le
terrain, pendant qu’une deuxième version est en cours de développement
• Une correction (patch) urgente est nécessaire: version 1.0.1

Début du Version Bug trouvé


travail sur la 1.0 sur le sur la
version 1.0 marché version 1.0 !
But:
version 2.0

Début du But: version


travail sur la 1.0.1 5
version 2.0
Contrôle des versions
3.Developpement

• Le contrôle des versions est fait par des outils qui:


• Gardent l’historique des changements sur les fichiers
• Aident à la coordination des différents développeurs qui travaillent sur des parties du système au
même moment

• Il existe des règles de versioning sémantique


• Version MAJOR: dans laquelle on fait des changements d’interface incompatibles avec les versions
précédentes
• Version MINOR: dans laquelle on rajoute de la fonctionnalité de manière compatible avec les
versions précédentes
• Version PATCH: dans laquelle on fait des corrections de bugs de manière compatible avec les
versions précédentes
• -alpha, -beta, -rc sont des étiquettes pour signaler des versions non-officielles (pre-release)
• E.g.: 1.0.0, 2.0.1, 3.0.0-alpha, 3.0.0-beta, 3.0.0-rc1, 3.0.0-rc2
6
• https://semver.org/
Contrôle des versions – solution au problème 1
3.Développement

2. Jim fait
3. Jim envoie au
quelques
1. Jim recupère serveur ses
changements au
(checkout) changements
code et les teste
File.java du (checkin)
serveur Le serveur de
contrôle des
versions cherche
et retourne la
version la plus 3.5 Une fois que
récente du fichier Jim fait un
aux développeurs checkin de ses
changements,
1.5 Le reste de l’équipe Serveur sur lequel l’équipe peut
peut récupérer la version 1 on exécute le récuperer une
logiciel de
du fichier File.java mise à jour du
contrôle des
pendant que Jim travaille versions serveur avec du
sur sa version nouveau code

D’autres développeurs 7
peuvent récupérer une
copie du fichier original
pendant que Jim travaille
sur sa machine locale
Contrôle des versions – solution au problème 2
3.Développement

• Par défaut, les logiciels de contrôle des versions donnent le code du tronc (e.g. head,
master, main)
• A chaque fois qu’on charge (commit) du code dans le système de contrôle, un numéro
de révision y est attaché
• Donc on doit retrouver quelle révision a été sortie sur le marché en tant que version 1.0
• On peut utiliser des tags pour identifier une version 8
Ce que fait le contrôle des versions
3.Développement

• Permet la création d’un dépôt pour garder le code dans un seul endroit, afin de faciliter la
gestion des artefacts
• Permet à plusieurs développeurs de récupérer des copies du code et de travailler
efficacement en tant qu’équipe
• Permet à plusieurs développeurs de pousser leurs changements dans le dépôt et de les
distribuer au reste de l’équipe
• Garde l’historique de qui change quoi, quand et pourquoi
• Gère des branches et des étiquettes (tags) sur le code, afin de faciliter la recherche des
versions et l’édition du code à partir d’une certaine version
• Annule les modifications qui n’aurait pas dû avoir lieu

• Ce que ne fait pas le contrôle des versions


• S’assure que le code compile
• Teste le code
• Réfléchit à notre place
• S’assure que le code est bien lisible et bien écrit 9
Types de Systèmes de contrôle des versions
• SCV – centralisées
• Nécessitent un serveur central qui doit être disponible
• Concurrent Version System (CVS),
• Subversion (SVN), Team Foundation Server (TFS), Perforce Helix
• SCVD – distribuées
• Bazaar, Mercurial, Git
• Le Serveur central est optionnel, les modifications sont enregistrées
localement, synchronisation plus tard

10
Sommaire
1. Contrôle des versions
2. Git, GitLab
3. Test, bugs, techniques de debug
4. Tests unitaires
5. Gestion du build, intégration logicielle

11

Ce chapitre a été rédigé par M. Vlad Valica


3.Développement
Git, GitLab, GitHub

Git = gestionnaire de versions distribué, libre, crée par Linus Torvalds (créateur du noyau Linux).
GitHub et GitLab = services en ligne permettant d’héberger des dépôts Git plus d’autres services.

Gestion centralisée

Il existe aussi la gestion décentralisée, pas dans le périmètre de ce cours


12

Git a été acheté par Microsoft.


3.Développement
Git en ligne de commande
Configurer git:

$ git config –global user.name ‘’Vlad Valica’’


$ git config –global user.email vlad.valica@ext.esiea.fr
$ git config –global --list

Démarrer un projet:
• Nouveau projet :
$ git init

• Cloner un repository :
$ git clone <remote-url>

13
Git: les zones de travail
3.Développement

• Zone de travail = dossier de travail


• Zone d’index (staging) = éléments choisis en vue de les sauvegarder dans le repository
• Dépôt local = votre dépôt de référence

14
3.Développement
Quelques commandes de Git

Voir l’état du projet :


$ git status

Ajoutez à la zone d’indexation (staging area) les nouveaux fichiers créés :


$ git add <files>

Retirer un fichier de la zone d’indexation :


$ git reset <nom de fichier>

Commit :
$ git commit –m « commentaire »

Voir les changements d’un fichier :


$ git diff <fichier> // affiche les modifications non indexées
$ git diff --cached <fichier> // affiche les modifications indexées
15
3.Développement
Git: structure du commit et branches

16
3.Développement
Git: naviguer dans l'historique (1/4)

Chaque fois que vous rajoutez un commit le HEAD se déplace sur


celui-ci.
Le HEAD est le pointeur qui représente votre état d’avancement
dans l’historique des commits.

Commit 2 HEAD, master

Commit 1

17
Git: naviguer dans l'historique (2/4)
3.Développement

Si jamais nous devons récupérer l’état du projet, tel qu’il était après le commit 1 :
$ git checkout <SHA_1 du commit 1>
$ git checkout 35d0a652ea931717ac20d54f1d4ad10c1772188c

Avant
Commit 2 master

Commit 1 HEAD

18
Après
Git: naviguer dans l'historique (3/4)
3.Développement

Si jamais nous devons récupérer l’état du projet, tel qu’il était après le commit 1 :
$ git checkout <SHA_1 du commit 1>
$ git checkout 35d0a652ea931717ac20d54f1d4ad10c1772188c

En effet, en revenant sur commit 1, nous sommes à l’instant ou le commit 2 n’existait


pas encore. Voici la sortie du git log :

Le HEAD pointe maintenant ce


commit 1.

19
Comment allons-nous faire pour revenir au commit 2 alors ?
Git: naviguer dans l'historique (4/4)
3.Développement

Comment allons-nous faire pour revenir au commit 2 ? (surtout que nous n’avons plus le SHA_1 du commit 2)

Le tag par défaut « master » pointe toujours vers le dernier commit. C’est donc master que nous allons utiliser.
$ git log //affiche uniquement les commit qui sont avant HEAD !

$ git checkout master


HEAD pointe maintenant vers le niveau master, donc dernier commit.

20
3.Développement
Git: les tags (1/2)
Les tags permettent d’identifier un état particulier ou une version / étape / milestone afin de
pouvoir y faire référence.

Quel but ?

V 1.0 est livré sur le terrain et un bug majeur


Commit k V 2.0 nécessite un patch. Il faut pouvoir reconstituer le
contexte de la V 1.0 afin de produire le patch.

Commit i V 1.0

Nous allons voir comment créer les tags et


comment les utiliser.
Commit2
21
Commit1
3.Développement
Git: les tags (2/2)
Créer des tags

Les tags sont créés au niveau du HEAD. Donc, si vous souhaitez créer un tag, il faut d’abord vous déplacer
sur le commit qui vous intéresse à tagguer.

$ git checkout <SHA_1>

Ensuite vous créez le tag :


$ git tag <nom_du_tag> -m’’Description du tag’’

Faites un $ git log pour voir le résultat.

Supprimer un tag :
$ git tag –delete <nom_du_tag>

Afficher tous les tags :


$ git tag 22
3.Développement
Git et GitLab (1/2)
GitLab est une application basée sur git offrant des services additionnels :
• partage du repository au sein d’une équipe
• outil graphique de travail avec git
• authentification des utilisateurs
• gestion de projet flexible
• gestion de bugs
• ...
Pourquoi GitLab ?

Car vous avez un serveur installé au sein de l’école et un compte gratuit en tant
qu’étudiant. Aller au http://gitlab.esiea.fr et vous connecter avec Azure AD

23
3.Développement
Git et GitLab (2/2)
• Issues utilisées pour tracer les bugs mais aussi les fonctionnalités.
• Github et GitLab vous permettent de faire le suivi entre les issues entrées et les
commits représentant la solution.

• Fork et pull request


• Afin de contribuer sur un projet disponible en lecture seule, il y a la possibilité de le « forker », cela
veut dire « copier » en tant que dépôt perso et avoir ainsi le droit d’écriture. Vous pouvez
désormais rajouter une nouvelle fonctionnalité à ce projet dans votre dépôt.
• Si vous souhaitez rendre la fonctionnalité utilisable pour tout le monde, vous devez avertir le
propriétaire du dépôt d’origine et lui proposer la nouvelle fonctionnalité. C’est ainsi que vous allez
faire votre pull request.

24
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (1/7)


• Le modèle distribué de git

Dev A Dev B

25
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (2/7)

26
Chaque dépôt local peut devenir un dépôt distant
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (3/7)

Cloner un dépôt distant

Pour cloner un dépôt distant :


$ git clone <remote> <folder_path>

27
Attention : vous avez un HEAD et main pour le dépôt local et un origin/HEAD et origin/main
pour le dépôt distant.
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (4/7)

Afficher les informations du dépôt distant utilisé :


$ git remote –v

Pour le dépôt local que vous avez fait, il n’y a pas encore de remote défini.

Pourquoi a-t-on besoin de rajouter un remote ?


(Pour être en mesure de pousser notre projet vers un serveur distant.)

Aller dans la racine du dépôt local et tapez :


$ git remote add origin <url_dépôt_distant>
28
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (5/7)

Push sur dépôt distant

➢ Authentification sur le dépôt distant


Générer un token sur gitlab :
https://gitlab.esiea.fr/<user>/<proj_name>/-/settings/access_tokens

➢ Push du code sur le dépôt distant :


$ git push https://<token>@gitlab.esiea.fr/<user>/<project>.git

➢ Push des tags sur le dépôt distant :


$ git push –tags

Ou, pour pousser un seul tag :


$ git push origin <TAG_NAME>

29
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (6/7)

Récupérer les commits d’un dépôt distant

30
3.Développement

Git, GitLab, GitHub: travail avec dépôt distant (7/7)

Récupérer les commits d’un dépôt distant

Pull copie les modifs du dépôt distant vers le dépôt


Fetch updates origin/master du dépôt local.
(Pull fait implicitement un fetch.) 31
local
3.Développement
Git, GitLab, GitHub: travail en équipe

32
3.Développement
Git, GitLab, GitHub: gestion centralisée (1/15)

Suivre les modifications

Lors du travail à plusieurs sur un projet, il est utile de pouvoir suivre les modifications apportées à un
fichier en particulier en cas de problème.

$ git blame <nom_du_fichier>

Ignorer des fichiers -> .gitignore


Créer le fichier .gitignore à la racine du projet (souvent créé par défaut dans le cadre d’un dépôt distant).
Rajouter les fichiers / dossiers à ignorer (regexp autorisées)

.gitignore

33
3.Développement
Git, GitLab, GitHub: gestion centralisée (2/15)

Mettre de côté mes modifications

Nous avons continué le dev sur notre fonctionnalité mais il arrive le besoin de devoir revenir sur un état du
projet relative à un tag ou un commit (via $ git checkout <tag_name>)

Quel serait le problème ? -> (perte de nos modifs) => le besoin de mettre de côté nos modifs

git stash
$ git stash save “fonctionnalité_1”
$ git stash save “fonctionnalité_2”

Afficher tous les stash :


$ git stash list

Voir les détail d’un stash :


$ git stash show <index_stash>

Pour injecter nos modifs : 34


$ git stash pop <index>
3.Développement
Git, GitLab, GitHub: gestion centralisée (3/15)
Merge des modifications

Maintenant nous travaillons à plusieurs sur le même fichier. Il y a une évolution faite par le Dev A et
sur la même portion de code, une autre faite par le Dev B.

Dev B finalise son dev, fait son $ git add, $ git commit.

Dev B fait ensuite $ git push et ainsi le


dépôt distant contient le code de Dev B.

Voyons ce qu’il se passe pour Dev A :

Dev A se retrouve dans une situation de


conflit.

35
3.Développement
Git, GitLab, GitHub: gestion centralisée (4/15)

Merge des modifications

Conflit : Une situation de conflit est une situation dans laquelle le même fichier et les mêmes lignes
de code ont été modifies par plusieurs développeurs. Si les modifs sont sur des fichiers différents, git
fusionnera les modifs.

Dev A ne peut pas faire git pull car il y a un conflit (nous sommes partis de l’idée que Dev B et
Dev A ont modifié le même fichier et même lignes de code).

Quelle solution ? (git stash…)


Dev A fait un git stash des ses modifs -> $git pull (qui marchera car plus de conflit) et ensuite
$ git stash pop

Lors du pop, git indiquera qu’un merge est requis pour le fichier concerné. (A voir lors du TD)

36
3.Développement
Git, GitLab, GitHub: gestion centralisée (5/15)
Merge des commits

Conflit : Une situation de conflit est une situation dans laquelle deux devs travaillent
ensemble sur le même git et font évoluer le même fichier. DEV A termine et commit ses
modifs. Par la suite DEV B termine également et veut faire commit de ses modifs.
Le conflit vient de l’historique des commits:

Le dépôt distant voit


comme enfant du
commit C le commit
D. Lors du push du
DEV B, il veut pousser
un commit E comme
enfant de commit C
or le dépôt distant a 37
déjà un enfant du
commit C !
3.Développement
Git, GitLab, GitHub: gestion centralisée (6/15)
Merge des commits

Voici la situation après git pull :

En cas de push, voici ce qu’il se passe :

38
3.Développement
Git, GitLab, GitHub: gestion centralisée (7/15)

Merge des commits

Par la suite, il faut résoudre les conflits de merge comme vu dans la partie précédente et faire le
commit de merge.

Vous pouvez à tout moment renoncer au merge et revenir à votre version :


$ git merge –abort

Après le merge, voici le résultat :

39
3.Développement
Git, GitLab, GitHub: gestion centralisée (8/15)
Merge des commits

Nous allons voir comment résoudre le merge d’une manière le plus souvent
automatique et qui ne génère pas de commit de merge.

$ git pull --rebase

40
3.Développement
Git, GitLab, GitHub: gestion centralisée (9/15)
Les branches

Une branche est un fil de commits.


Une branche que vous connaissez déjà est “master”.
Dans le cadre du travail en équipe sur des fonctionnalités réparties, le travail par branche permet
de paralléliser le développement sans risqué d’interférence.

Une fois le travail fini, la fonctionnalité


stabilisée, le travail réalisé sur branche
sera porté sur la branche master.

Création d’une branche :


$ git branch <BRANCH_NAME>

Afficher les branches :


$ git branch 41
3.Développement
Git, GitLab, GitHub: gestion centralisée (10/15)
Les branches

Récupérer une branche :

$ git checkout –b <BRANCH_NAME>


Ou
$ git switch –c <BRANCH_NAME>

Push et creation de la branch locale sur le dépôt distant :

$ git push –set-upstream origin <BRANCH_NAME>

Récupérer une branche du dépôt distante sur un dépôt local different de celui qui l’a créée:

$ git pull
$ git branch –a
$ git checkout <BRANCH_NAME>
42
Copier un commit d’une branche à une autre :
$ git cherry-pick <SHA_1_COMMIT_TO_COPY>
3.Développement
Git, GitLab, GitHub: gestion centralisée (11/15)

Les branches

Faire un merge entre deux branches :


Il faut se positioner sur la branche principale (master) (là où nous souhaitons rapatrier les modifications).
$ git checkout –b <BRANCH_NAME>

Disons que nous souhaitons rapatrier les modifications de la branche BRANCH_DEV_A sur la master.
Alors en étant sur la master :
$ git merge BRANCH_DEV_A

S’il y a conflit, le message dira quells sont les fichiers à merger manuellement comme vous le savez déjà.
Après le merge, n’oubliez pas de commit vos modifications manuelles.

43
3.Développement
Git, GitLab, GitHub: gestion centralisée (12/15)

Les branches

Faire un rebase entre deux branches :


Nous partons de cette situation : Nous souhaitons arriver à cette situation :

44
Rebase = déplacer le point de départ d'une branche, en
l'amenant à un autre point de l'arborescence
3.Développement
Git, GitLab, GitHub: gestion centralisée (13/15)

Les branches
1. Aller sur la branche que vous souhaitez rebaser :
Faire un rebase entre deux branches $ git checkout <BRANCH_NAME>
Nous partons de cette situation : 2. Porter le contenu de la branche vers le master :
$ git rebase master
1. Résoudre les merges manuels si jamais il y en a.
2. $ git add <manually_merged_filename>
3. $ git rebase –continue

45
Rebase = déplacer le point de départ d'une branche, en
l'amenant à un autre point de l'arborescence
3.Développement
Git, GitLab, GitHub: gestion centralisée (14/15)

Les branches 1. Aller sur le master : $ git checkout master


2. Merger les branches : $ git merge <BRANCH_NAME>
Nous sommes là :
Voici le résultat :

46
3.Développement
Git, GitLab, GitHub: gestion centralisée (15/15)
Dev A ne peut pas faire git pull car il y a un conflit (nous
sommes partis de l’idée que Dev B et Dev A ont modifié le
même fichier et même lignes de code.

Aperçu du projet: Quelle solution ? (git stash…)


$ git status Dev A fait un git stash des ses modifs -> $ git pull (qui
marchera car plus de conflit) et ensuite
$ git stash pop

Lors du pop, git indiquera qu’un merge est requis pour le


fichier concerné.
$ git config --global core.editor notepad
$ git log --graph --oneline --decorate --color –all

Alias:
$ git config --global alias.lol "log --graph --oneline --decorate --color --all“
$ git lol
47
$ git log <BRANCH_NAME>
Git GUI applications client
3.Développement

• Git est integré dans tous les IDEs importants (IntelliJ, Eclipse, Visual Studio...)
• Il y a aussi la possibilité d’utiliser des outils graphiques pour le travail avec Git.

Aller à : https://git-scm.com/download/gui/windows

Comment choisir ?
• Plateformes supportées selon vos besoins : Windows, Linux, MacOS
• Prix : les différentes applications sont gratuites mais pas toutes.
• Type de licence

Recommandation multi-plateforme:
• Git Extensions: https://gitextensions.github.io/

Built-in git tools :


• Git-gui : https://git-scm.com/docs/git-gui
• Gitk : https://git-scm.com/docs/gitk

Linux : Gitg : https://wiki.gnome.org/Apps/Gitg/


48
Windows : TortoiseGit : https://tortoisegit.org/
Exercices Contrôle des Versions
3.Developpement

49
Sommaire
1. Contrôle des versions
2. Git, GitLab
3. Tests, bugs, techniques de debug
4. Tests unitaires
5. Gestion du build, intégration logicielle

Jeff Langr, 50
Andrew Hunt,
David Thomas
Test Logiciel 4. Test

Vérification:
un système est conforme à ses spécifications
Validation:
un système respecte les besoins (exigences) du
client

51
Test Logiciel 4. Test

• Les activités de vérification:


• Revue des exigences, revue de la conception, lecture et inspection de code, revue du plan de
test…
• Les tests de bas niveau: tests unitaires, tests d’intégration
• Tests système: on teste le système complet, conformité par rapport aux exigences
• Les activités de validation:
• Tests d’acceptance (recette), du point de vue du client
• Essais client: le système est installé chez quelques clients avant d’être mis sur le marché
52
Les étapes du test 4. Test

• Tests unitaire (développement)


• Les classes, les méthodes sont testées indépendamment
• Tests boîte blanche
• Tests d’intégration
• Les composants individuels sont testés indépendamment
• Les interfaces entre les composants sont testées
• Les spécifications d’interface sont très utiles pour ce type
de test
• Tests boîte grise
• Tests d’acceptance ou tests système
• Des tests sont exécutés afin de s’assurer que le système
complet est fonctionnel
• Tests boîte noire
53
Les bugs (1/3)
4. Test

• Un bug logiciel est une erreur, un défaut ou une faute dans un programme ou dans
un système
• Le bug mène à:
• Des résultats incorrects ou inattendus
• Des comportements inattendus
• Les bugs sont découverts en testant à des différents niveaux
• Certains bugs sont découverts par les clients du logiciel
• Ce sont les plus ennuyeux
• Un bug trouvé par des tests unitaires coute beaucoup moins cher qu’un bug trouvé
par le client!
• Il existe des suites logicielles qui permettent la gestion des bugs
• Helix ALM
54
Les bugs (2/3)
4. Test

• Exemple de description de bug dans une application de gestion de test

ID 3001
Description Après trois échecs consécutifs, on peut continuer à essayer de se connecter
Tracé vers SPEC_99
Comment Rentrer trois fois de suite une combinaison nom utilisateur/mot-de-passe inconnue. Il est
reproduire possible de tenter de login autant de fois qu’on veut.
Etat Assigné
Trouvé dans V1.1
Sévérité S1 S1/S2/S3/S4
Priorité P1 P1/P2/P3/P4
Analyse
Corrigé dans version 55

Trouvé par Matthieu


Les bugs (3/3)
4. Test

Sévérité Signification
S1 Une fonctionnalité très importante (critique) du programme ne fonctionne pas. Pas de solution de
contournement. Ex: les crash
S2 Une fonctionnalité ne rend pas le résultat attendu. Il existe une solution de contournement
S3 Une fonctionnalité n’est pas conforme aux standards et aux conventions. On peut faire autrement
S4 Défauts cosmétiques qui n’affectent pas la fonctionnalité.

Priorité Signification
P1 Showstopper. Doit être corrigé pour l’itération (sprint) courante. Rien ne peut continuer
P2 Major/High. Doit être corrigé pour la version courante. Le produit n’est pas acceptable avec ce bug.
P3 Average/Medium. Devrait être corrigé pour la version courante. Le produit est acceptable avec ce
bug.
56
P4 Minor/Low. Peut ne pas être corrigé, si on n’a pas le temps
Comment éviter les bugs
4. Test

• DRY (don't repeat yourself)


• Ne pas dupliquer du code
• Echouer rapidement (fail fast)
• Eviter les numéros magiques
• Ne pas réutiliser les variables
• Ne pas modifier les paramètres des méthodes (déclarez-les final)
• Utiliser des types immuables (la valeur de la variable ne change pas une fois créée)
• Ex. String
• Utiliser des référents immuables (déclarées final)

57
Comment minimiser les bugs (1/2)
4. Test

• On vérifie les préconditions d'exécution correcte d'une méthode


=> programmation défensive

/**
* @param x requires x >= 0
* @return approximation to square root of x
*/
public double sqrt(double x) {
if (! (x >= 0)) throw new AssertionError();
...
}

• On développe de manière itérative


• On écrit un peu de code
• On le teste (test unitaire, test de non-régression sur tout le code)
58
• On continue
Comment minimiser les bugs (2/2)
4. Test

• Modularité
• Diviser un programme en composants (modules) et classes qui peuvent être
conçus, implémentés, testés et réutilisés séparément du reste
• Encapsulation
• Le module est responsable de son comportement interne et d'autres parts du
programme ne peuvent pas détruire son intégrité
• Minimiser la portée des variables
int i;
for (i = 0; i < 100; ++i) { ... } for (int i = 0; i < 100; ++i) { ... }
• Déclarer une variable quand on l'utilise la première fois et dans le block
ou elle est nécessaire
59
Comment trouver les bugs (1/2)
4. Test

• Reproduire le bug: trouver la séquence de code la plus petite qui permet de reproduire le
problème
• Méthode pour localiser le bug et sa cause:
• Étudier les données en entrée du test qui reproduit le bug, ainsi que les résultats incorrects, les
exceptions, les stack traces
• Emettre une hypothèse sur l'emplacement du bug (où il peut être, où il ne peut pas être)
• Expérimenter pour tester l'hypothèse, obtenir des informations concernant le problème
• Exécuter un autre test
• Rajouter des System.out.println, des assertions
• Utiliser des points d'arrêt (breakpoints)
• Répéter en rajoutant les données de l'expériment aux données initiales et émettre une
nouvelle hypothèse; normalement à ce stade les localisations possibles du bug devraient être
réduites

60
Comment trouver les bugs (2/2)
4. Test

• D'autres techniques
• Utiliser une autre classe (par exemple, remplacer ArrayList par LinkedList)
• Utiliser une autre implémentation d'algorithme (recherche dichotomique/recherche linéaire)
• Utiliser un autre OS, un autre hardware (si on suspecte un problème de ce type)
• Prioriser les hypothèses: le vieux code a moins de chances d'avoir un bug s'il a déjà été testé;
l'API Java a moins de bugs que notre nouveau code etc

• Utiliser le debugger de l'IDE

61
Comment corriger les bugs
4. Test

• Eviter les corrections trop rapides


• Se poser les questions:
• Erreur de codage ou erreur de design?
• Si erreur de design: étudier si le design doit être refait ou au moins regarder si le bug n'apparait pas
dans d'autres classes similaires
• Est-ce que le même code qui cause le bug se trouve-t-il ailleurs ?
• Est-ce que ma correction va créer d'autres problèmes ailleurs?
• Après la correction, exécuter tous les tests de non-regression

• Les mettre dans les Release Notes:


• https://youtrack.jetbrains.com/articles/IDEA-A-126713932

62
Exercices Tests & Bugs
4. Test

63
Sommaire
1. Contrôle des versions
2. Git, GitLab
3. Tests, bugs, techniques de debug
4. Tests unitaires
5. Gestion du build, intégration logicielle

Jeff Langr, 64
Andrew Hunt,
David Thomas
Tests Unitaires

• Un test unitaire est un bout de code qui adresse une zone de fonctionnalité
très petite, très spécifique, du code qui est testé

• Les tests unitaires sont exécutés afin de démontrer que le code fait ce que le
développeur croit qu’il doit faire

Tests unitaires
• En Java: JUnit
• https://junit.org/junit5/ ,

• https://en.wikipedia.org/wiki/List_of_unit_testing_frameworks
Tests Unitaires
• Exemples de situations qui doivent être testées avec des tests unitaires:
• Si on insère une valeur grande dans une liste triée, on teste que la valeur est bien insérée au bon
endroit
• Si une méthode implémente l’addition des entiers, alors elle doit fonctionner correctement avec
n’importe quel nombre entier, y compris des zéros et des nombres négatifs
• Une méthode qui implémente la division doit être robuste à la division par zéro

Tests unitaires
• Une boucle qui parcourt un tableau ne doit jamais aller au-delà du nombre d'éléments du
tableau
• Le code ne doit pas crasher si une variable a la valeur null

• Le but des tests unitaires est de rendre la vie du développeur plus facile
• Ils améliorent la conception et réduisent drastiquement la quantité de temps passée à
corriger les bugs
66
JUnit 5
•Test APIs:
• modules that we use for writing tests
• they provide the programming model for a
particular Test Engine (for example, junit-jupiter-
api for JUnit 5 tests and junit for JUnit 4 tests).
•Test Engines:
• allow to execute a kind of test (Jupiter tests,
legacy JUnit 4, or other Java tests) within the
JUnit Platform.
• created by extending the general Platform
Engine (junit-platform-engine).
•Test Launcher:
• modules that provide the ability of test
discovery inside the JUnit platform for external
build tools and IDEs.
• this API is consumed by tools such as Maven,
Gradle, IntelliJ, … using the junit-platform-
launcher module
67
Structure générique des tests unitaires

Tests unitaires
68
JUnit 5 - Cycle de vie du test Autres Annotations:
@DisplayName
@Disabled
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; @Test
import org.junit.jupiter.api.Test; void testOne() {
System.out.println("TEST 1");
}
class LifecycleJUnit5Test {
@BeforeAll @Test
static void setupAll() { void testTwo() {
System.out.println("Setup ALL TESTS in the class"); System.out.println("TEST 2");
}
}
@AfterEach
@BeforeEach void teardown() {
void setup() { System.out.println("Teardown EACH TEST in the class");
System.out.println("Setup EACH TEST in the class"); }
}
@AfterAll
static void teardownAll() { 69
System.out.println("Teardown ALL TESTS in the class");
}
}
JUnit 5 - Assertions import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class BasicOperationsTest {


public class BasicOperations { BasicOperations bo = new BasicOperations();
public int addition(int x, int y) { @Test
return x + y; public void testAddition() {
} int r = bo.addition(3, 1);
public int substraction(int x, int y) { assertEquals(r, 4);
return x - y; //fail("Not yet implemented");

Tests unitaires
} }
} @Test
public void testSubstraction() {
int r = bo.substraction(3, 1);
assertEquals(r, 2);
//fail("Not yet implemented");
}
@Test
public void testSubstractionWithZero() {
70
//fail("Not yet implemented");
}
}
Junit 5 - Assertions
package demo;
import java.util.ArrayList;

public class Largest {


public static Integer largest(ArrayList<Integer> list) {
int index, max = Integer.MAX_VALUE;
for (index = 0; index < list.size() - 1; index ++) { TROUVEZ LE BUG !
if (list.get(index) > max)
max = list.get(index);
}

Tests unitaires
return max; package demo;
} import java.util.ArrayList;
import org.junit.jupiter.api.Test;
}
import static org.junit.jupiter.api.Assertions.assertEquals;

public class TestLargest {


@Test
public void testSimple() {
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(3); arr.add(7); arr.add(9);
assertEquals(9, Largest.largest(arr).intValue());
}
}
71

https://junit.org/junit5/docs/5.8.2/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html
Junit 5 – Assertions: testing Exceptions
public class BasicOperations {
public int division(int x, int y) throws IllegalArgumentException {
if (y == 0) throw new IllegalArgumentException("division by zero");
return x/y;
}
}
public class BasicOperationsTest {
BasicOperations bo = new BasicOperations();

Tests unitaires
@Test
void testDivision() {
Throwable ex = assertThrows(IllegalArgumentException.class, () -> bo.division(3, 0));
assertEquals("division by zero", ex.getMessage());
}

@Test
void testDivideIntInt() {
try {
bo.division(3, 0);
} catch (Throwable ex) {
assertTrue(ex instanceof IllegalArgumentException); 72
assertEquals(ex.getMessage(), "division by zero");
}
}
}
JUnit 5
Jupiter tests peuvent être exécutés de plusieurs façons:
• En utilisant un outil de build:
• Maven (implémenté dans le module junit-plaform-surefire-provider)
• Gradle (implémenté dans lemodule junit-platform-gradle-plugin)
• En utilisant le Console Launcher:
• Une application Java en ligne de commande qui permet de lancer la plateforme JUnit au
terminal
• En utilisant un IDE:
• IntelliJ (depuis version 2016.2)
• Eclipse (depuis version 4.7, Oxygen).

73
Quoi tester? right-BICEP (1/5)

• Utilise ton right-BICEP:


• right: est-ce que tes résultats sont corrects (right)?
• B: est-ce que les résultats sont corrects aux limites (boundary)?
• I: est-ce que tu peux vérifier les relations inverses?
• C: est-ce que tu peux faire une vérification (cross-check) de tes résultats en utilisant
d’autres moyens?

Tests unitaires
• E: est-ce que tu peux forcer l’apparition des conditions d’erreur?
• P: est-ce que les exigences de performance sont-elles respectées?

74
Quoi tester? right-BICEP (2/5)

• Est-ce que tes résultats sont corrects?


• Pose-toi la question: comment pourrais-je savoir que le code s’exécute correctement?
• Teste ce qui est spécifié

• Est-ce que les résultats sont corrects aux limites? Teste avec:

Tests unitaires
Valeurs boguées ou incohérentes, e.g. nom de fichier « !*W:X\& @#<«
• Des données mal formatées, comme une adresse email sans domaine: peter@food.
• Des valeurs vides ou manquantes: 0, 0.0, null
• Des valeurs loin des limites normales (e.g. un âge de 10000 ans)
• Des valeurs dupliquées dans des listes qui ne devraient pas avoir des dupliquées
• Des listes triées qui ne le sont pas et vice-versa
• Des choses qui arrivent dans un ordre inattendu, e.g. sélection avant log-in

75
Quoi tester? right-BICEP (3/5)

• Vérifier les relations inverses


• Certaines méthodes peuvent être vérifiées en calculant leur inverse logique

public void testSquareRootUsingInverse() {


double x = mySquareRoot(5.0);
assertEquals(5.0, x*x, 0,0001);

Tests unitaires
}

• On peut vérifier que les données ont été bien insérées dans une base de données en les
cherchant

• Vérifier les résultats en utilisant d’autres moyens


• Utilise un algorithme différent pour calculer une quantité (même s’il est moins performant)
76
Quoi tester? right-BICEP (4/5)

• Forcer l’apparition des conditions d’erreur


• Vérifie les choses qui peuvent apparaître sur un système réel
• Le disque se remplit, interruption de réseau, crash du programme, out-of-memory,
le système est chargé, la palette de couleur est limitée, la résolution vidéo est très
basse ou très haute
• Utilise des objets fantaisie (mock objects)

Tests unitaires
77
Mock objects (1/4)

• L’objectif des tests unitaires est de tester une méthode à la fois


• Que se passe-t-il quand la méthode dépend d’autres choses: réseau, base de données,
logiciel embarqué…

• On crée un objet fantaisie (Mock), qui est un remplaçant de l’objet réel

Tests unitaires
• Un Mock est utile dans les situations suivantes:
• L’objet réel a un comportement non-détérministe (e.g. l’évolution d’une valeur en bourse)
• L’objet réel est difficile de mettre en place
• L’objet réel a un comportement qui est difficile à simuler (e.g. erreur réseau)
• L’objet réel est lent
• L’objet réel a une interface graphique ou est lui-même une interface graphique
• L’objet réel n’existe pas encore

78
Mock objects (2/4)

• Comment créer un mock:


• On utilise une interface pour décrire les services rendus par l’objet réel
• On implémente l’interface pour le code de production
• On implémente l’interface dans un mock pour les tests

Tests unitaires
79
Problématique: on veut tester TimeChecker mais on n’a pas de contrôle sur les valeurs
retournées par RealSystemTime
Mock objects (3/4)
import java.util.ArrayList;
public interface Measurable {
public class TimeChecker {
public long getTime();
} private ArrayList<Long> recordedEventTimes;
public class RealSystemTime implements private Measurable timer;
Measurable{
public long getTime() { public TimeChecker(Measurable t) {
return System.currentTimeMillis(); recordedEventTimes = new
ArrayList<Long>();

Tests unitaires
}
timer = t;
} }
public class MockSystemTime implements
Measurable { public void recordTime() {
private long currentTime; Long val =
public long getTime() { Long.valueOf(timer.getTime();
recordedEventTimes.add(val);
System.out.println(currentTime);
}
return currentTime;
} public ArrayList<Long> getEventTimes() {
public void setTime(long time) { return recordedEventTimes;
currentTime = time; }
} 80
}
}
Mock objects (4/4)
import static org.junit.Assert.*;
import org.junit.Test;
import java.util.ArrayList;
public class TestTimeChecker {
public class TimeChecker {
@Test
private ArrayList<Long> recordedEventTimes; /** test that times are recorded */
private Measurable timer; public void testRecordTime() {
MockSystemTime timer = new MockSystemTime();
public TimeChecker(Measurable t) { TimeChecker tc = new TimeChecker(timer);

Tests unitaires
recordedEventTimes = new ArrayList<Long>();
timer = t; timer.setTime(1);
} tc.recordTime();
public void recordTime() {
timer.setTime(2);
Long val = Long.valueOf(timer.getTime();
recordedEventTimes.add(val); tc.recordTime();
}
assertEquals(tc.getEventTimes().get(0).longValue(), 1);
public ArrayList<Long> getEventTimes() { assertEquals(tc.getEventTimes().get(1).longValue(), 2);
return recordedEventTimes; }
} }
} 81
Quoi tester? right-BICEP (5/5)
• Exigences de performance
• On ne teste pas la performance, mais ses tendances quand la taille des entrées croît, la
complexité augmente etc

public void testURLBlockingClass() {


Timer timer new Timer();
String bad_url = "http://testurl.com";
//check the bad url against a small list of known bad urls

Tests unitaires
URLFilter filter1 = new URLFilter (small_list);
timer.start();
filter1.check(bad_url);
timer.end();
assertTrue(timer.elapsedTime() < 1.0);
//check the bad url against a uge list of known bad urls
URLFilter filter2 = new URLFilter (huge_list);
timer.start();
filter2.check(bad_url);
timer.end();
assertTrue(timer.elapsedTime() < 3.0);
} 82
Développement piloté par les tests (TDD)

• Est une pratique de l'Extreme Programming


• On écrit les tests unitaires, ils échouent tous
• On implémente le code afin de faire passer les tests
• On fait du refactoring*, on corrige le code si les tests échouent

Tests unitaires
• On répète (cycle de développement très court)

1.Write
the tests

2. Test
5. Refactor
fails

4.Test 3. Write 83
passes code
*réusinage = restructuration du code sans rajouter des
fonctionnalités, sans corriger des bugs
Exercices Tests Unitaires
3.Developpement

84
Sommaire
1. Contrôle des versions
2. Git, GitLab
3. Tests, bugs, techniques de debug
4. Tests unitaires
5. Gestion du build, intégration logicielle

1. Spécifications 2. Architecture
et Conception

Génie Logiciel 85
5. Maintenance 3. Développement
4. Test
3.Développement
Gestion du Build

• Un outil de build est un outil qui automatise la construction du projet logiciel.


• La construction d’un projet logiciel inclut typiquement une ou plusieurs de ces
activités:

Génération de Génération de la
code source Compilation
documentation

Installation des d‘autres activités


Paquetage du
paquets de code qui peuvent être
code compilé en
sur un serveur ou automatisées…
JAR, DLL, LIB, ZIP…
ailleurs
86
Gestion du Build
3.Développement

• Les avantages de l’automatisation du processus de build:


• Minimise le risque que les humains fassent des erreurs lorsqu’ils construisent le logiciel
manuellement
• Typiquement plus rapide qu’un humain qui exécute les mêmes étapes manuellement
• Répétabilité parfaite

• Outils: make, Apache Ant, Maven, MsBuild

87
3.Développement
Gestion du Build - Maven
• Contient des cycles, des phases et des buts (goals) dans le processus de build
• Si les dépendances ne sont pas trouvées dans le dépôt local, Maven les télécharge depuis un dépôt
(repository) central
• Des plugin de build sont utilisés afin d’insérer des buts supplémentaires dans une phase de build
(ils sont définis dans le fichier POM).
• Les profiles permettent d’exécuter des builds différents (e.g. sur un PC de développement et sur un
serveur de build)
Fichier pom.xml

Cycles de Build
Maven • Phases
• Buts
1. Lit le fichier pom.xml Dépôt local
Dépendances (.jar)
2. Télécharge les dépendances dans le dépôt local Maven
3. Exécute des cycles, des phases et/ou des buts de build
4. Exécute des plugins Plugins de build
… conformément au profile de build sélectionné
88
Profiles de build
https://maven.apache.org/
https://download.eclipse.org/technology/m2e/releases/
Gestion du Build - Maven 3.Développement

• Le plus simple pom.xml (http://maven.apache.org/pom.html)

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-
4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>fr.esiea.tools</groupId>
<artifactId>UnitTests</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
89
Rajouter Junit 5 dans Maven 3.Développement

<build>
<dependencies> <plugins>
<dependency> <plugin>
<groupId>org.junit.jupiter</groupId> <artifactId>maven-surefire-plugin</artifactId>
<artifactId>junit-jupiter-api</artifactId> <version>${maven-surefire-plugin.version}</version>
<version>${junit.jupiter.version}</version> <dependencies>
<scope>test</scope> <dependency>
</dependency> <groupId>org.junit.platform</groupId>
</dependencies> <artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit.platform.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
90

https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api
Gestion du Build - Maven
3.Développement

Phase de Build Description

Valide que le projet est correct et que toute l’information nécessaire pour le build y est. On
validate
assure que les dépendances sont téléchargées et disponibles.

compile Compile le source code du projet.

Exécute les tests unitaires sur le code compile en utilisant le framework de test unitaires
test
déclaré. Ces tests ne devraient pas nécessiter que le code soit déjà paqueté ou déployé.

package Crée les paquets de code compilé dans leur format de distribution, par exemple JAR.

install Installe les paquets dans le dépôt local, afin d’être utilisés localement.

Copie le paquet final sur un dépôt à distance, afin de le partager avec d’autres
deploy
développeurs et projets.
91
3.Développement
Intégration continue
• Le processus de build est automatisé
• A chaque fois que le code source est modifié, une nouvelle version du logiciel
complet est compilée, testée et on crée les paquets

Automatisé Manuel

Développeurs Code source Serveur de Build Test Production


. Compilation d’acceptance
. Test unitaire
. Test
d’intégration 93
. Paquetage
Exercices Build et Integration
3.Developpement

96
97
Les perspectives de Test
4. Test

• Les Utilisateurs voient le system de l’extérieur


• Ils voient la fonctionnalité du système
• Le système est une boîte noire pour eux
• Les Testeurs jettent un coup d’œil dans le système
• Ils cherchent la fonctionnalité
• Ils vérifient que les choses se passent comme elles devraient se passer
• Le système est une boîte grise pour eux
• Les Développeurs voient les détails
• Conception de classes, patrons, code dupliqué: le système est complètement ouvert
• Le système est une boîte blanche pour eux

• Les tests doivent se placer dans ces trois perspectives 98


4. Test
Les tests boîte noire

• Se concentrent sur les entrées et les sorties


• Fonctionnalité: vérifier que les données rentrent et sortent du système tel que c’est exprimé
dans la User Story ou dans la spécification
• Validation des entrées utilisateur: e.g. tester la valeur -1 pour une quantité d’argent
• Validation des sorties: s’assurer que tous les chemins fonctionnels ont été testés
• Transitions d’état: s’assurer que le système va d’état en état tel que c’est défini
• Les cas limites et les erreurs des valeurs proches: e.g. vérifier le mois 13

input output
99
Les tests boîte grise
4. Test

• Sont similaires aux tests boîte noire mais on peut vérifier que le système travaille
conformément aux spécifications au-delà de l’interface utilisateur:
• Les logs et les audits: en utilisant un outil de visualisation des logs ou un outil de rapport d’audit
ou en faisant des requêtes de bases de données
• Les données destinées aux autres systèmes: vérifier le format des données de sortie,
l’information sécurisée doit être encryptée…
• Information rajoutée par le système: images, sommes de contrôle, checksums, dates écrites
dans le format correct
• Des résidus : les données sont supprimées quand elles doivent l’être, pas de fuite de mémoire,
la désinstallation laisse le système propre…

input output
Log sample 1
Log sample 2
Log sample 3 100
Les tests boîte blanche 4. Test

• On doit connaître le code et on cherche:


• A tester toutes les branches différentes du code: vérifier tous les if/else et switch/case; les
chemins secondaires
• Traitement correct des erreurs
• Le code fonctionne comme c’est spécifié (e.g. méthodes thread-safe)
• La gestion correcte des contraintes des ressources: que se passe-t-il si la mémoire, l’espace
disque, les connexions réseau ne sont pas disponibles comme on le suppose?

public static void main (String[] args)


{
int n = Integer.parseInt(args[0]);
int fact = 1;
while (n > 1)
fact *= n--; 101
}
}
Automatisation des tests
4. Test

• Il existe des frameworks pour automatiser les tests


• Le support pour l’automatisation des tests fait partie du framework
• Les tests spécifiques sont écrits par les développeurs
• Avantages de l’automatisation:
• Les tests sont plus efficaces
• Ils sont toujours exécutés de la même manière
• La vérification de la non-regression est possible
• Les tests sont exécutés à chaque fois que le code est buildé

102

Vous aimerez peut-être aussi