but3-cours-maitenance-appli.../cours1-bonne-pratique-tdd/cours1.md
Maxime Pierront 685d89af75 🎉
2024-03-09 14:37:52 +01:00

552 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
marp: true
paginate: true
---
# R6.06. Maintenance applicative
## Cours 1
---
# Présentation
## Maxime Pierront - [contact@pierrontmaxi.me](mailto:contact@pierrontmaxi.me)
---
# Qui êtes-vous ?
---
# Qu'est-ce que vous connaissez en bonne pratique de code ?
---
# Programme du module
* Bonne pratique de code
* Refactoring
* TDD
* BDD
* Tests e2e
* Projet commun cf **développment avancé** (effectué par *Félix*)
---
# Les bases
## Avis personnel
### 3 types de développeur :
* Pisseur de code (code monkey)
* Développeur
* Développeur Produit
---
# Les bases
## Principe SOLID
### SOLID est un acronyme qui représente cinq principes de conception et de programmation orientée objet. Ils sont les suivants :
* Principe de responsabilité unique (**S**RP)
* Principe ouvert/fermé (**O**CP)
* Principe de substitution de Liskov (**L**SP)
* Principe de ségrégation d'interface (**I**SP)
* Principe d'inversion de dépendance (**D**IP)
---
# Principe de responsabilité unique (SRP)
Une classe ne devrait avoir qu'une seule raison de changer. Cela signifie qu'une classe ne devrait avoir qu'un seul travail ou responsabilité.
```js
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
// Cette classe a une seule responsabilité : gérer les informations de l'employé
getName() {
return this.name;
}
getEmail() {
return this.email;
}
}
```
---
# Principe ouvert/fermé (OCP)
Les entités logicielles (classes, modules, fonctions, etc.) devraient être ouvertes à l'extension mais fermées à la modification. Cela signifie que vous devriez pouvoir ajouter de nouvelles fonctionnalités sans modifier le code existant.
---
```js
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Circle {
constructor(radius) {
this.radius = radius;
}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
class ShapeCalculator {
// Cette méthode est ouverte à l'extension (nouvelles formes) mais fermée à la modification
calculateTotalArea(shapes) {
let totalArea = 0;
for (const shape of shapes) {
totalArea += shape.getArea();
}
return totalArea;
}
}
```
---
# Principe de substitution de Liskov (LSP)
Les sous-types doivent être substituables à leurs types de base. Cela signifie que si un programme utilise une classe de base, il devrait être en mesure d'utiliser n'importe laquelle de ses sous-classes sans que le programme ne le sache.
---
```js
class Animal {
makeSound() {}
}
class Dog extends Animal {
makeSound() {
console.log("Woof!");
}
}
class Cat extends Animal {
makeSound() {
console.log("Meow!");
}
}
function makeAnimalSound(animal) {
// Cette fonction peut accepter n'importe quel objet de type Animal ou de ses sous-classes
animal.makeSound();
}
const dog = new Dog();
const cat = new Cat();
makeAnimalSound(dog); // Affiche "Woof!"
makeAnimalSound(cat); // Affiche "Meow!"
```
---
# Principe de ségrégation d'interface (ISP)
Les clients ne devraient pas être forcés de dépendre d'interfaces qu'ils n'utilisent pas. Cela signifie qu'une classe ne devrait pas avoir à implémenter des méthodes qu'elle n'utilise pas. Au lieu d'une interface volumineuse, plusieurs interfaces plus petites sont préférables.
---
```js
class Bird {
fly() {}
makeSound() {}
}
class Penguin {
// Les pingouins ne peuvent pas voler, donc cette classe ne devrait pas avoir à implémenter la méthode fly()
makeSound() {
console.log("Squawk!");
}
}
interface Flyable {
fly(): void;
}
interface SoundMaker {
makeSound(): void;
}
class Bird implements Flyable, SoundMaker {
fly() {
console.log("Flying!");
}
makeSound() {
console.log("Chirp!");
}
}
class Penguin implements SoundMaker {
makeSound() {
console.log("Squawk!");
}
}
```
---
# Principe d'inversion de dépendance (DIP)
Les modules de haut niveau ne devraient pas dépendre des modules de bas niveau. Les deux devraient dépendre d'abstractions. Cela signifie que vous devriez dépendre d'abstractions, pas de concrétions.
---
```js
class DataService {
getData() {
// Récupère les données à partir d'une source externe (par exemple, une base de données)
return ["data1", "data2", "data3"];
}
}
class DataProcessor {
// Cette classe dépend d'une abstraction (l'interface DataService) plutôt que d'une implémentation concrète
constructor(dataService) {
this.dataService = dataService;
}
processData() {
const data = this.dataService.getData();
// Traite les données
return data.map((item) => item.toUpperCase());
}
}
const dataService = new DataService();
const dataProcessor = new DataProcessor(dataService);
const processedData = dataProcessor.processData();
console.log(processedData); // Affiche ["DATA1", "DATA2", "DATA3"]
```
---
# Complexité Cyclomatique:
La complexité cyclomatique est une métrique logicielle utilisée pour indiquer la complexité d'un programme. Elle a été introduite par Thomas J. McCabe en 1976 comme une façon de quantifier le nombre de chemins linéairement indépendants à travers le code source d'un programme. Plus la complexité cyclomatique est élevée, plus le programme est complexe et plus il sera difficile à comprendre, à tester et à maintenir.
IntelliJ Plugin for Java : [Code Metrics](https://plugins.jetbrains.com/plugin/12159-codemetrics)
---
# Complexité Cyclomatique:
Voici quelques lignes directrices pour interpréter la complexité cyclomatique :
• Une complexité cyclomatique de 1 indique que le programme ne contient aucune décision (instructions if, boucles, etc.) et est donc simple et facile à comprendre.
• Une complexité cyclomatique de 2-10 indique que le programme contient quelques décisions, mais reste relativement simple et facile à comprendre.
• Une complexité cyclomatique de 11-20 indique que le programme est modérément complexe et peut être difficile à comprendre.
• Une complexité cyclomatique de 21-50 indique que le programme est complexe et sera difficile à comprendre et à tester.
---
# KISS
**KISS** est un acronyme pour **"Keep It Simple, Stupid"** (en français, "Garde ça simple, stupide"). C'est un principe de conception qui encourage à garder les choses aussi simples que possible, sans ajouter de complexité inutile.
Voici quelques façons d'appliquer le principe KISS dans votre code :
1. **Éviter la complexité inutile** : Évitez d'ajouter des fonctionnalités inutiles ou des niveaux d'abstraction supplémentaires si cela ne rend pas le code plus facile à comprendre ou à maintenir.
2. **Utiliser des noms significatifs** : Utilisez des noms de variables, de fonctions et de classes qui reflètent clairement leur rôle et leur signification dans le code. Les noms doivent être explicites et éviter les abréviations.
---
3. **Écrire du code court et simple** : Écrivez du code court et simple qui est facile à comprendre en un coup d'œil. Évitez les longues fonctions et les classes complexes.
4. **Éviter les optimisations prématurées** : Évitez d'optimiser prématurément le code, car cela peut ajouter de la complexité inutile. Attendez d'avoir identifié les goulots d'étranglement avant d'optimiser.
5. **Respecter les conventions de codage** : Respectez les conventions de codage de votre langage et de votre équipe pour rendre le code plus facile à lire et à comprendre.
---
# Le développement piloté par les tests (Test-Driven Development - TDD)
C'est une méthodologie de développement logiciel qui consiste à écrire des tests avant d'écrire le code correspondant.
---
# Voici les étapes de base du TDD :
1. **Écrire un test** : Écrivez un test qui définit le comportement attendu d'une fonctionnalité ou d'une partie du code. Le test doit être suffisamment détaillé pour spécifier toutes les entrées et sorties attendues.
2. **Exécuter le test (RED)**: Exécutez le test pour vous assurer qu'il échoue. Si le test réussit, cela signifie que la fonctionnalité est déjà implémentée ou que le test est incorrect.
3. **Écrire le code (GREEN)** : Écrivez le code nécessaire pour faire réussir le test. Le code doit être aussi simple que possible et ne doit implémenter que la fonctionnalité requise par le test.
4. **Refactoriser le code (REFACTOR)** : Refactorisez le code pour améliorer sa structure et sa lisibilité, tout en vous assurant que les tests continuent de réussir.
5. **Répéter**: Répétez les étapes précédentes pour chaque fonctionnalité ou partie du code à implémenter.
---
# Le TDD présente plusieurs avantages :
1. **Amélioration de la qualité du code** : En écrivant des tests avant le code, vous vous assurez que le code est testable et que toutes les fonctionnalités sont couvertes par des tests. Cela peut réduire le risque d'introduire des bugs et améliorer la qualité globale du code.
2. **Réduction du temps de développement** : Le TDD peut réduire le temps de développement en vous aidant à vous concentrer sur la fonctionnalité à implémenter et en réduisant le temps de débogage.
---
# Le TDD présente plusieurs avantages :
3. **Facilitation de la maintenance** : Les tests peuvent servir de documentation pour le code et faciliter la maintenance en fournissant une description claire du comportement attendu du code.
4. **Amélioration de la conception** : Le TDD peut améliorer la conception du code en encourageant la séparation des préoccupations et en favorisant une conception modulaire et flexible.
Le TDD peut nécessiter un certain investissement initial en temps et en effort, mais il peut offrir des avantages significatifs en termes de qualité, de productivité et de maintenance du code.
---
# Librairies de tests unitaires en javascript
*avec taux de rentention en %*
* **Vitest** (97%)
* Testing Library (95%)
* **Jest** (89%)
* Jasmine (54%)
Source : State JS 2022
---
# Exemple TU
* Création dossier du projet : `mkdir but3-tdd`
* Se mettre dans le dossier : `cd but3-tdd`
* Initialisation du projet : `npm init -y`
* Installation de jest : `npm install --save-dev jest`
* Modification de `package.json` dans la section `scripts` :
```json
"scripts": {
"test": "jest"
}
```
* Création dossier `src` et `__tests__` :
* `mkdir src`
* `mkdir __tests__`
---
# Exemple TU
Dans `src` créer le fichier `sum.js` suivant :
```js
function sum(a, b) {
return a + b;
}
module.exports = sum;
```
---
# Exemple TU
Dans `__tests__` créer le fichier `sum.test.js` suivant :
```js
const sum = require('../src/sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
```
Lancer le test avec `npm test`
---
# Exercice Fizz Buzz via TDD
Ecrire un programme qui retourne les entiers de 1 à 100,
A prendre en compte :
* Pour les multiples de 3, remplacez le nombre par "Fizz"
* Pour les multiples de 5, remplacez le nombre par "Buzz"
* Pour les multiples de 15, remplacez le nombre par "FizzBuzz"
Exemple :
*"12Fizz4BuzzFizz78FizzBuzz............Buzz"*
---
# Correction exercice Fizz Buzz
---
# Exercice sur la suite de Conway via TDD
Ecrire un programme qui permet d'écrire N étape de [la suite de Conway](https://fr.wikipedia.org/wiki/Suite_de_Conway).
Exemple :
```js
const test1 = lookAndSaySequence(2);
console.log(test1)
// 1
// 11
const test2 = lookAndSaySequence(4);
console.log(test2);
// 1
// 11
// 21
// 1211
```
---
# Correction exercice la suite de Conway
---
# Behavior Driven Development (BDD)
Le **BDD** est une approche de développement logiciel qui se concentre sur la collaboration entre les membres de l'équipe pour comprendre et définir le comportement attendu du logiciel.
Le **BDD** se base sur les principes du **TDD** (Test Driven Development), mais avec une approche plus orientée vers la communication et la collaboration entre les différents intervenants (développeurs, testeurs, chefs de projet, utilisateurs finaux, etc.).
---
# Le processus de BDD se déroule en trois étapes :
1. Définir les comportements attendus du logiciel sous forme de scénarios concrets et compréhensibles par tous les intervenants.
2. Écrire des tests automatisés qui vérifient que les comportements définis dans les scénarios sont correctement implémentés dans le code.
3. Implémenter le code qui répond aux comportements attendus et passer les tests avec succès.
Le BDD encourage la communication et la collaboration entre les différents membres de l'équipe pour s'assurer que le logiciel répond aux besoins réels des utilisateurs finaux. Il permet également de réduire les risques d'erreurs et de malentendus en clarifiant les exigences et en les traduisant en scénarios concrets.
---
# Libraire pour faire du BDD
## Cucumber
Cucumber est un outil open source utilisé pour faire du Behavior Driven Development (BDD). Il permet de décrire les comportements attendus d'une application sous forme de scénarios écrits en langage naturel, accessibles à tous les membres de l'équipe (développeurs, testeurs, chefs de projet, utilisateurs finaux, etc.).
---
# Cucumber
Les scénarios Cucumber sont écrits dans un format appelé Gherkin, qui utilise une syntaxe simple et lisible pour décrire les étapes d'un scénario. Chaque scénario est composé de trois parties :
**Given** : décrit l'état initial du système avant que l'utilisateur n'effectue une action.
**When** : décrit l'action effectuée par l'utilisateur.
**Then** : décrit le résultat attendu après l'action de l'utilisateur.
---
# Exemple fichier feature
```feature
Feature: FizzBuzz
En tant qu'utilisateur,
Je veux pouvoir générer une liste d'entiers de 1 à 100
Avec les règles suivantes :
- Pour les multiples de 3, remplacez le nombre par "Fizz"
- Pour les multiples de 5, remplacez le nombre par "Buzz"
- Pour les multiples de 15, remplacez le nombre par "FizzBuzz"
Scenario: Générer la liste FizzBuzz
Given que je génère la liste d'entiers de 1 à 100
When j'applique les règles FizzBuzz
Then je devrais obtenir la liste suivante :
| 1 | 2 | Fizz | 4 | Buzz | Fizz | 7 | 8 | Fizz | Buzz | 11 | Fizz | 13 | 14 | FizzBuzz | ... |
```
---
# Ajouter cucumber sur le projet
Ajout de cucumber : `npm install --save-dev @cucumber/cucumber`
Ajout d'un nouveau script dans `package.json` :
```js
"scripts": {
"test": "jest",
"behavior": "npx cucumber-js"
}
```
Création du fichier à la racine `cucumber.js` avec le contenu suivant :
```js
module.exports = {
default: {
}
};
```
---
# Ajout fichier feature
Création dossier `features` à la racine
Création dossier support : `features/support`
Dans `features` créer le fichier `sum.feature` suivant :
```feature
Feature: Sum two numbers
Scenario: Add two numbers
Given I have a sum function
When I add 1 and 2
Then the result should be 3
```
---
# Ajout de la glue
Dans `support` créer le fichier `sum-steps.js` suivant :
```js
const { Given, When, Then } = require('@cucumber/cucumber');
const assert = require('assert');
const sum = require('../../src/sum');
let result;
Given('I have a sum function', function () {
// This step is more for context, no action needed
});
When('I add {int} and {int}', function (a, b) {
result = sum(a, b);
});
Then('the result should be {int}', function (expectedResult) {
assert.strictEqual(result, expectedResult);
});
```
---
# Lancer le test de comportement
Commande : `npm behavior`
---
# Calculateur de TVA en BDD
**Consignes :**
Écrire un programme qui calcule la TVA d'un produit en fonction de son prix hors taxe et du taux de TVA en vigueur.
Le programme doit afficher le prix TTC (toutes taxes comprises) du produit.
Le taux de TVA peut être modifié par l'utilisateur.
---
# Correction calculateur de TVA en BDD
---
# Le loup, la chèvre et le chou en BDD
**Consignes :**
Écrire un programme qui simule le passage du loup, de la chèvre et du chou d'une rive à l'autre d'une rivière à l'aide d'un bateau.
* Le bateau ne peut transporter qu'un seul passager à la fois, à l'exception du fermier qui peut le conduire.
* Le loup ne peut pas être laissé seul avec la chèvre, sinon il la mangera.
* La chèvre ne peut pas être laissée seule avec le chou, sinon elle le mangera.
* Le programme doit afficher les étapes nécessaires pour amener les trois passagers sur l'autre rive en toute sécurité.
---
# Correction le loup, la chèvre et le chou en BDD
---
# Recap
* Bonne pratique de code
* SOLID
* KISS
* Complexité cyclomatique
* TDD
* BDD
---
## Maxime Pierront - [contact@pierrontmaxi.me](mailto:contact@pierrontmaxi.me)