La documentation dans un context agile est un vrai défi.
Les évolutions constantes rendent les mises à jour chronophage pour un résultat souvent insuffisant.
Il faut repenser le problème autrement.
La combinaison des approches Living Documentation et Approval Testing ouvre une nouvelle voie.
En utilisant les tests comme des générateurs de documentation,
ils valident des comportements représentés sous la forme la plus adaptée à leur compréhension.
Vous allez découvrir comment cette approche,
en remettant à plat la gestion de la documentation, des spécifications et des tests pour les penser comme un seul et même élément,
permet de favoriser le dialogue entre les intervenants, de réduire les coûts de maintenance et d'assurer une parfaite synchronisation avec le code.
Vous repartirez de cette session avec de nouvelles perspectives pour vos tests et votre documentation
ainsi que les bases pour expérimenter.
3. @sebfauvel Et s’il suffisait de documenter pour tester ?
Sébastien
Fauvel
@sebfauvel
4. @sebfauvel Et s’il suffisait de documenter pour tester ?
Agenda
- Maintenir une application
- Ce que j’ai découvert
- Mise en place
- Cas d’utilisation
- Et pour vous ?
5. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une
application
6. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application
Patrimoine
7. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application
Documenter
8. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Documenter
Documentation as code
Fichier Asciidoc
Liste des valeurs par défaut:
- WIDTH: "1200"
- HEIGHT: "600"
- COLOR: "blue"
Liste des valeurs par défaut:
* WIDTH: "1200"
* HEIGHT: "600"
* COLOR: "blue"
9. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Documenter
Living documentation
Génération du fichier Asciidoc
String doc = "Liste des valeurs par défaut:nn";
for (Option option : Option.values()) {
doc += String.format("* %s: "%s"n", option.name(), option.value);
}
10. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Documenter
Living documentation
11. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application
Tester
12. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Tester
Simple test
@Test
public void default_values() {
assertEquals("1200", Option.WIDTH),
assertEquals("600", Option.HEIGHT),
assertEquals("blue", Option.COLOR)
}
13. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Tester
Découvrir les valeurs
Test source
Test result
@Test
public void discover_option_default() {
assertEquals("????",
String.join(", ",
Option.WIDTH.value,
Option.HEIGHT.value,
Option.COLOR.value));
}
org.opentest4j.AssertionFailedError:
Expected :????
Actual :1200, 600, blue
14. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Tester
Approvals
Test source
received.txt
Approvals.verify(String.join(", ",
Option.WIDTH.value,
Option.HEIGHT.value,
Option.COLOR.value));
1200, 600, blue
15. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Tester
Approvals
16. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application
Si on allait plus loin…
17. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Si on allait plus loin…
Mise en forme
Amélioration de la lisibilité (nom des attributs, séparation des cas,
données de test, …
)
Test source
received.txt
String format = "* %s: "%s"";
String obtain = Arrays.stream(Option.values())
.map(option -> String.format(format,
option.name(),
option.value))
.collect(Collectors.joining("n"));
Approvals.verify(obtain);
* WIDTH: "1200"
* HEIGHT: "600"
* COLOR: "blue"
18. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Si on allait plus loin…
Passage à AsciiDoc
received.adoc
Liste des valeurs par défaut:
- WIDTH: "1200"
- HEIGHT: "600"
- COLOR: "blue"
Liste des valeurs par défaut:
* WIDTH: "1200"
* HEIGHT: "600"
* COLOR: "blue"
19. @sebfauvel Et s’il suffisait de documenter pour tester ?
Maintenir une application: Si on allait plus loin…
Tester avec une documentation vivante
20. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
21. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Ça marche
22. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Simplicité
Un test "presque" standard
Fichier asciidoc Rendu
2 + 3 = 5
@Test
public void should_be_5_when_adding_2_and_3() {
int a = 2;
int b = 3;
int result = a + b;
String text = String.format("%d + %d = *%d*", a, b, result);
Approvals.verify(text);
}
2 + 3 = *5*
23. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Découverte des valeurs à l’exécution
Regroupement des valeurs par type de résultat
Listes des valeurs (entre 1 et 30) par résultat obtenu:
- Buzz: [5, 10, 20, 25]
- Fizz: [3, 6, 9, 12, 18, 21, 24, 27]
- FizzBuzz: [15, 30]
- Number: [1, 2, 4, 7, 8, 11, 13, 14, 16, 17, 19, 22, 23, 26, 28, 29]
24. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Un affichage orienté utilisateur sans contraintes
Game of Life
Meurt avec moins de 2
voisins
Avec moins de 2 voisins,
une cellule meurt à la
génération suivante.
* * *
* * *
* * *
*
⇒
*
* * *
* * *
* * *
Reste en vie avec 2 voisins
Une cellule vivante reste
en vie à la génération
suivante lorsqu’elle a 2
voisins.
* * *
* * *
* * *
*
⇒
*
* * *
* * *
* * *
Nait avec 3 voisins
Une cellule morte avec 3
voisins, devient vivante à
la génération suivante.
* * *
* * *
* * *
*
⇒
*
* * *
* * *
* * *
25. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Pas besoin de tout afficher
Points d’expérience pour passer au niveau suivant
Au bout d’un certain nombre de points d’expérience, le héros passe
au niveau supérieur.
Il a besoin de plus en plus de points
d’expérience pour passer au niveau supérieur.
Le nombre de points requis pour changer de niveau est :
Niveau
initial
Nouveau
niveau
Total de points
depuis le niveau 0
Points depuis le
dernier niveau
0 1 52 +52
1 2 139 +87
2 3 262 +123
3 4 421 +159
4 5 617 +196
5 6 851 +234
6 7 1125 +274
7 8 1442 +317
8 9 1806 +364
9 10 2223 +417
( ) + (35 ⋅ level) + 50
1.55
level
26. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Tester des éléments visuels
Affichage d’un item selon sa production
Configuration générale:
- Quantité minimale avant d’afficher le texte: 3
- Nombre maximum d’items à afficher: 5
Quantité 0 1 2 3 4 5 6 7 8 9 10
Quantité positive
Quantité négative
Avec une capacité potentielle(8)
27. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Afficher les données utiles sans les dupliquer
Grouper par 'country'
- Australia
- Racks Jacson
- France
- Marc Durand
- Pierre Martin
{
"users": [
{
"userId": 1,
"firstName": "Pierre",
"lastName": "Martin",
"country": "France",
"emailAddress": "krish.lee@learningcontainer.com"
},
{
"userId": 2,
"firstName": "Racks",
"lastName": "Jacson",
"country": "Australia",
"emailAddress": "racks.jacson@learningcontainer.com"
},
{
"userId": 3,
"firstName": "Marc",
"lastName": "Durand",
"country": "France",
"emailAddress": "marc.durand@learningcontainer.com"
}
}
28. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Afficher les données utiles sans les dupliquer
Trier par 'lastname'
- Marc Durand
- Racks Jacson
- Pierre Martin
{
"users": [
{
"userId": 1,
"firstName": "Pierre",
"lastName": "Martin",
"country": "France",
"emailAddress": "krish.lee@learningcontainer.com"
},
{
"userId": 2,
"firstName": "Racks",
"lastName": "Jacson",
"country": "Australia",
"emailAddress": "racks.jacson@learningcontainer.com"
},
{
"userId": 3,
"firstName": "Marc",
"lastName": "Durand",
"country": "France",
"emailAddress": "marc.durand@learningcontainer.com"
}
}
29. @sebfauvel Et s’il suffisait de documenter pour tester ?
Ce que j’ai découvert
Générer plusieurs documentations
- Sélection des chapitres à inclure
- Plusieurs structurations
possibles
- Niveaux de détails adaptés
Manuel utilisateurs
Documentation
technique
Configuration
Développeurs
Utilisateurs
Nouveaux arrivants
31. @sebfauvel Et s’il suffisait de documenter pour tester ?
Mise en place
Pas de big bang
- Possibilité de commencer sur un cas isolé
- Applicable quelque soit la technologie
- On peut démarrer avec une mise en oeuvre minimaliste
32. @sebfauvel Et s’il suffisait de documenter pour tester ?
Mise en place
Approvals
C#, C++, Go, Java, JavaScript, Kotlin, Lua,
Objective-C, PHP, Perl, Python, Ruby, Swift
https://approvaltests.com/
@Test
public void should_be_5_when_adding_2_and_3() {
int a = 2;
int b = 3;
int result = a + b;
String text = String.format("%d + %d = *%d*", a, b, result);
Approvals.verify(text);
}
33. @sebfauvel Et s’il suffisait de documenter pour tester ?
Mise en place
DocumentationTesting
Java, Python
https://sfauvel.github.io/documentationtesting/
@RegisterExtension
static ApprovalsExtension doc = new SimpleApprovalsExtension();
@Test
public void my_first_test() {
String text = "hello";
doc.write(String.format(""%s".toUpperCase() => "%s"",
text,
text.toUpperCase()));
}
34. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation
35. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation
Donner de la visibilité à vos tests
- Ecrire dans un fichier depuis vos tests
- Nom de la méthode
- Données d’entrée
- Appels de méthode
- Valeurs ou états finaux
36. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation: Donner de la visibilité à vos tests
find error in a tag
Message
Validator: UPPERCASE
Errors
- Tag:TAG_ABC
- Positions:
- 45 - 49: [TAG_ABC ] Text With a problem
- 81 - 88: [TAG_DEF ] Another Problem in text
Error content
[CONTRACT X] TEST
[TAG_ABC ] Text With a problem
[TAG_DEF ] Another Problem in text
[TAG_XVF ] IT346047234767
Text With a problem
Another Problem in text
37. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation
Sur du code existant
- Générer des cas
- Mettre en forme le résultat
- Comprendre le fonctionnement
38. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation: Sur du code existant
Fichier de référence sur Gilded Rose
+5 Dexterity Vest,10,20
+5 Dexterity Vest,9,19
+5 Dexterity Vest,8,18
+5 Dexterity Vest,7,17
+5 Dexterity Vest,6,16
+5 Dexterity Vest,5,15
+5 Dexterity Vest,4,14
+5 Dexterity Vest,3,13
+5 Dexterity Vest,2,12
+5 Dexterity Vest,1,11
+5 Dexterity Vest,0,10
+5 Dexterity Vest,-1,8
+5 Dexterity Vest,-2,6
+5 Dexterity Vest,-3,4
+5 Dexterity Vest,-4,2
+5 Dexterity Vest,-5,0
+5 Dexterity Vest,-6,0
+5 Dexterity Vest,-7,0
+5 Dexterity Vest,-8,0
+5 Dexterity Vest,-9,0
https://kata-log.rocks/gilded-rose-kata
39. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation: Sur du code existant
Evolution d’un article: +5 Dexterity Vest
Tous les articles ont une VendreAvant qui indique le nombre de jours que
l’on a pour vendre l’article
Tous les articles ont une Qualité qui indique la valeur de l’article
A la fin de chaque jour, notre système décrémente ces deux valeurs pour
chaque article
Dès que la date de vente est dépassée, la Qualité décroit deux fois plus
vite
La Qualité d’un article n’est jamais négative
La Qualité d’un article n’est jamais supérieure à 50
iteration 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
sellIn 10 9 8 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9
qualities 20 19 18 17 16 15 14 13 12 11 10 8 6 4 2 0 0 0 0 0
40. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation
Exemple Mapping
41. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation: Exemple Mapping
Gherkin
Feature: Book train ticket
Train tickets can be booked in advance, though we do reserve some
capacity in each coach for people who have tickets but didn't
reserve a seat.
Background:
Given a train with the following layout:
| coach | seats |
| 1 | 10 |
| 2 | 10 |
Rule: Max 70% of entire train can be booked
This allows people who show up just before the train leaves
to get a seat.
Example: train has enough space left
Given the train has the following available seats:
| coach | available seats |
| 1 | 5 |
| 2 | 6 |
When I book a reservation for 2 people
42. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation: Exemple Mapping
Book train ticket
Train tickets can be booked in advance, though we do reserve some
capacity in each coach for people who have tickets but
didn’t
reserve a seat.
Rule: Max 70% of entire train can be booked
This allows people who show up just before the train leaves
to get a seat.
Example 1. Train has enough space left
Train before reservation
☒☒☒☐☐
☒☒☐☐☐
☒☒☐☐☐
☒☒☐☐☐
I book a reservation for 2 people
Reservation is confirmed
Train after reservation
☒☒☒☒☐
☒☒☒☐☐
☒☒☐☐☐
☒☒☐☐☐
43. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation
TDD
44. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation: TDD
Extrait de la documentation de la classe
.
Extraction du corps d’une méthode
Le code source d’une méthode peut être extrait à partir de l’objet method
ou à partir de
la classe et du nom de méthode.
Il est également possible de n’extraire que le corps de
la méthode.
Extraction du corps de la méthode
Code source de la classe d’origine
Code extrait
CodeExtractor
String code = CodeExtractor.extractMethodBody(SimpleClass.class, "simpleMethod");
public class SimpleClass {
public int simpleMethod() {
return 0;
}
}
return 0;
45. @sebfauvel Et s’il suffisait de documenter pour tester ?
Cas d’utilisation
Tout n’est pas simple
- Organiser la documentation
- Changement de paradigme (penser
rédaction)
- Gérer l’affichage peut complexifier les tests
- Identifier ce qui est testé dans la texte de ce
qui est simplement écrit
- Retrouver l’origine du texte pour le modifier
47. @sebfauvel Et s’il suffisait de documenter pour tester ?
Et pour vous ?
Avoir une visibilité sur le patrimoine de “tests”
- La documentation constitue l’ensemble des tests
- L’information est accessible
- Les cas décrits sont compréhensibles par tout le monde
48. @sebfauvel Et s’il suffisait de documenter pour tester ?
Et pour vous ?
Améliorer la compréhension entre les équipes
- Les fonctionnalités sont décrites à travers des exemples
concrets
- Permet le Behavior Driven Development sans la
contrainte Gherkin
- Autorise des représentations visuelles
49. @sebfauvel Et s’il suffisait de documenter pour tester ?
Et pour vous ?
Reprendre la main sur du legacy
- Rapidité de mise en oeuvre de tests de non régression
avec le Golden Master
- Capacité à rendre compréhensible et maintenable ce
fichier de référence pour en faire une vraie
documentation
- Conserver la connaissance acquise et en faire un support
de discussion pour tous les intervenants
50. @sebfauvel Et s’il suffisait de documenter pour tester ?
Et pour vous ?
Retour sur investissement
- Mutualisation de l’effort de maintenance entre les tests
et la documentation
- Documentation mise à jour automatiquement
- Validation des tests par contrôle visuel plus rapide
- Possibilité d’avoir des documentations ciblées sans
surcoût de maintenance
51. @sebfauvel Et s’il suffisait de documenter pour tester ?
A vous de jouer