-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathTDD.qmd
157 lines (129 loc) · 9.37 KB
/
TDD.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# Développement piloté par les tests {#sec-tdd}
Si l'on écrivait des logiciels pouvant se tester automatiquement?
Le développement piloté par les tests (en anglais *test-driven development, TDD*) est une pratique populaire et intéressante.
Il s'agit d'écrire des logiciels avec un composant d'autovalidation (des tests automatisés).
Mais écrire beaucoup de tests n'est pas toujours une tâche agréable pour les développeurs et développeuses.
Historiquement, si l'on attend la fin d'un projet pour écrire des tests, il ne reste plus beaucoup de temps, et l'équipe laisse tomber les tests.
Pour pallier ce problème, le développement piloté par les tests propose de travailler **en petits pas**, c'est-à-dire écrire un test simple (en premier), puis écrire la partie du logiciel pour passer le test de manière simple (la plus simple).
Ça fait moins de codage entre les validations et c'est possiblement même plus stimulant pour les développeurs et développeuses.
Ainsi, il y a toujours des tests pour les fonctionnalités, et le développement se fait en petits incréments qui sont validés par les tests.
Faire les *petits pas* réduit le risque associé à de gros changements dans un logiciel sans validation intermédiaire.
Au fur et à mesure qu'on développe un logiciel, on développe également quelques tests de ce dernier.
Puisque les tests sont automatiques, ils sont aussi faciles à exécuter que le compilateur.
Il y a une discipline imposée dans le TDD qui nécessite d'écrire un *test en premier*, c'est-à-dire *avant* d'écrire le code.
La démarche est illustrée sur la @fig-TDD_states.
Beaucoup d'outils (IDE) favorisent ce genre de développement.
Nous pouvons écrire un test qui appelle une fonction qui n'existe pas encore, et l'IDE va nous proposer un squelette de la méthode, avec les arguments et une valeur de retour.
Une personne pure et dure du TDD insistera sur le fait que le test soit écrit toujours en premier!
Cette discipline est souvent culturelle.
Plusieurs chercheurs et chercheuses ont mené des expériences, par exemple @Karac_TDD_2018, pour voir si *tester en premier* avait un vrai bénéfice.
Les résultats de leurs analyses n'ont pas toujours montré que c'est le cas (ce genre d'expérience est difficile à faire, en partie parce qu'il n'y a pas beaucoup de développeurs et développeuses en industrie qui le pratiquent).
Les chercheurs et chercheuses ont trouvé que faire un petit test *après* avoir écrit le code a aussi un bénéfice sur le plan de la qualité.
Dans tous les cas, des chercheurs et chercheuses ont trouvé que le fait de travailler en *petits pas* apporte *toujours* un avantage sur le plan de la qualité.
Travailler en *petits pas* est utile, même sans faire du TDD de manière dogmatique.
Sachez qu'il existe beaucoup de cadriciels (en anglais *frameworks*) pour faciliter l'exécution automatique des tests réalisés dans le cadre du TDD.
Pour Java, il y a JUnit, mais il y en a pour pratiquement tous les langages et les environnements.
En ce qui concerne le squelette pour le laboratoire, il s'agit de [Jest](https://jestjs.io/).
<!-- La version Python utilise [PyTest](https://docs.pytest.org/en/latest/). -->
L'exécution de tests peut même être faite à chaque *commit* du code dans un dépôt comme GitHub.
Il est possible de mesurer la [couverture de code](https://fr.wikipedia.org/wiki/Couverture_de_code)\ {{< fa brands wikipedia-w >}}\ atteinte par les tests (mais ce sujet sort du cadre de la matière de ce manuel).
```{.plantuml genurl="true" #fig-TDD_states caption="États du développement piloté par les tests." }
@startuml
!include normal.pumlinclude
hide empty description
skinparam StateBorderColor black
skinparam StateFontColor white
skinparam StateFontStyle bold
skinparam StateFontSize 16
skinparam StateArrowFontSize 16
'title États du développement piloté par les tests
state Rouge as "Au moins\nun test\néchoue" #red
state Vert as "Tous\nles tests\npassent" #green
state PasTDD as "<color red>Pas TDD\n<color red><size:48><&thumb-down></color>" #yellow
[*] -right-> Rouge : Créer\nnouveau\ntest
Rouge -r-> Vert : Écrire code\npour faire\npasser test(s)<sup>‡</sup>
Vert -l-> Rouge : \nCréer\nnouveau\ntest
Vert -> Vert : <color green>Faire une activité</color>\n<color green>de réusinage<sup>†</sup></color>
Vert --> PasTDD : Écrire code sans test
legend center
<sup>†</sup>Une activité de réusinage n'est pas censée causer
un problème avec un test. Cependant, cela peut
arriver que les tests ne passent plus, et il faudra
corriger des problèmes.
<sup>‡</sup>Il peut arriver qu'un test soit mal codé (il contient
un bogue). Dans ce cas, on corrige le code du test.
end legend
@enduml
```
Les activités de réusinage sont expliquées dans la section [Réusinage (Refactorisation)](#sec-Refactoring).
## Kata TDD
Pour apprendre à faire du développement piloté par les tests (et pour apprendre les cadriciels supportant l'automatisation des tests), il existe une activité nommée "kata TDD".
*Kata* est un terme japonais désignant une séquence de techniques réalisée dans le vide dans les arts martiaux japonais.
[En voici une vidéo](https://www.youtube.com/watch?v=DmPk_A-RU50)\ {{< fa brands youtube >}}.
C'est un outil de transmission de techniques et de principes de combat.
![Étudiante de karaté faisant le kata *Bassai Dai* (photo ["Karate"](https://www.flickr.com/photos/the-consortium/4507180302/) [(CC BY-SA 2.0)](https://creativecommons.org/licenses/by-sa/2.0/) par [The Consortium](https://www.flickr.com/people/the-consortium/)).](images/karate.jpg){#fig-KataBasaiDai width=30%}
Alors, le "kata TDD" a été proposé par Dave Thomas, et le but est de développer la fluidité avec le développement piloté par les tests.
Un kata TDD se pratique avec un IDE (environnement de développement logiciel) et un support pour les tests (par exemple JUnit).
Pratiquer le même kata peut améliorer votre habileté de programmation.
On peut pratiquer le même kata dans un langage différent, avec un IDE différent ou avec un environnement de test différent.
Le kata vous permet d'avoir une facilité avec les aspects techniques de développement dans plusieurs dimensions (complétion de code pour le test et pour l'application, interface API du cadriciel de test, etc.).
En plus, les activités de réusinage sont normalement intégrées dans un kata.
Le fait de travailler en *petits pas* peut faire en sorte que la dette technique s'accumule.
Les IDE facilitent l'application des activités de réusinage.
Un langage fortement typé comme Java permet d'avoir plus de fonctionnalités automatisées de réusinage dans un IDE qu'un langage dynamique comme JavaScript ou Python.
Une activité de base de réusinage est le renommage d'une variable ou d'une fonction.
Le réusinage rend le code plus facile à comprendre et à maintenir.
### Exemple de kata TDD FizzBuzz
L'inspiration de cet exercice vient de [codingdojo.org](http://codingdojo.org/kata/FizzBuzz/).
Dans cet exercice, il faut écrire selon le développement piloté par les tests un programme qui imprime les nombres de 1 à 100.
Mais, pour les multiples de trois, il faut imprimer `Fizz` au lieu du nombre et, pour les multiples de cinq, il faut imprimer `Buzz`. Pour les nombres étant des multiples de trois et de cinq, il faut imprimer `FizzBuzz`. Voici un exemple des sorties:
```
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
... etc. jusqu'à 100
```
#### Préalables
Il faut installer un IDE qui supporte les activités de réusinage (*refactorings*) comme Visual Studio Code, Eclipse, IntelliJ, etc., puis un cadriciel de test (JUnit, Mocha/Chai, Jest, unittest, etc.).
Pour un exemple qui fonctionne en TypeScript, vous pouvez cloner le code à partir de [ce dépôt](https://github.com/fuhrmanator/kata_jest_ts_fizzbuzz).
#### Déroulement
Cet exercice peut se faire individuellement ou en équipe de deux.
En équipe, une personne écrit le test, et l'autre écrit le code pour passer le test (c'est la variante ping-pong).
Chaque personne réfléchit aux activités de réusinage éventuelles lorsque le projet est dans l'état vert (@fig-TDD_states).
Les membres de l'équipe peuvent changer de rôle (testeur ou testeuse, codeur ou codeuse) après un certain nombre d'étapes, ou après avoir terminé le kata entier.
Pour respecter la philosophie de *petits pas,* il vaut mieux:
- ne lire que l'étape courante;
- ne travailler que sur l'étape courante;
- ne faire que les tests avec les entrées valides.
#### Kata pour FizzBuzz
Les étapes sont simples et précises.
Il s'agit de créer une classe ayant une méthode acceptant un entier et retournant une valeur selon les exigences de FizzBuzz décrites plus haut.
1. Un argument de 1 retourne `1`.
2. Un argument de 2 retourne `2`.
3. Un argument de 3 retourne `Fizz`.
4. Un argument de 6 retourne `Fizz`.
5. Un argument de 5 retourne `Buzz`.
6. Un argument de 10 retourne `Buzz`.
7. Un argument de 15 retourne `FizzBuzz`.
8. Un argument de 30 retourne `FizzBuzz`.
9. Supporter des exigences qui évoluent. Attention aux conflits dans les exigences:
a. Il faut imprimer Fizz au lieu du nombre si le nombre est un multiple de 3 ou contient un 3 (ex.: 13 → `Fizz`).
b. Il faut imprimer Buzz au lieu du nombre si le nombre est un multiple de 5 ou contient un 5 (ex.: 59 → `Buzz`).
c. Il faut imprimer FizzBuzz si le nombre est un multiple de 5 et de 3 ou contient un 5 et un 3 (ex.: 53 → `FizzBuzz`).