La programmation par les tests

Tags :
  • MON
  • 2023-2024
  • temps 3
  • tests
  • programmation
  • python
Auteurs :
  • Lucie Le Boursicaud

Ce MON va m'apprendre à programmer à l'aide de tests en utilisant le language Python.

Niveau intermédiaire

Connaitre python.

Introduction

Pour ce MON je vais donc me baser sur le cours Programmation par les tests de François Brucker. On utilisera tout au long des chapitres le language Python.

Sommaire

  1. Cours de François Brucker
  2. Exercices

C'est quoi la programmation par les tests ?

La programmation par tests (Test-Driven Development ou TDD en anglais), c'est une approche de développement logiciel où les tests sont écrits avant même que le code de production ne soit implémenté. Pour mettre en place cette méthode on suit généralement trois étapes :

  1. Écriture du test (Red) : Dans cette première étape, on rédige un test unitaire qui définit le comportement attendu d'une fonction ou d'un module. À ce stade, le code de production n'existe pas encore, donc le test échouera initialement.

  2. Implémentation du code de production (Green) : Après avoir écrit le test, on crée le code minimal nécessaire pour faire passer le test avec succès. L'objectif est de produire un code qui satisfait les exigences du test, sans ajouter de fonctionnalités supplémentaires, seulement le nécessaire.

  3. Refactoring (Refactor) : Une fois que le test passe avec succès, on peut ensuite réorganiser et améliorer le code sans altérer son comportement externe. Cela implique souvent d'éliminer les duplications de code, d'améliorer la lisibilité ou d'optimiser les performances.

Ce cycle se répète continuellement, chaque nouvelle fonctionnalité ou modification du code étant accompagnée de l'écriture de nouveaux tests, garantissant ainsi que les changements ne causent pas de régressions ou de dysfonctionnements dans le système existant.

Pourquoi c'est bien ?

La programmation par tests présente plusieurs avantages :

Ainsi, la programmation par tests est une méthode de développement qui vise à améliorer la qualité du code en écrivant des tests automatisés avant d'écrire le code de production, favorisant ainsi la fiabilité, la détection précoce des erreurs et une meilleure conception logicielle. C'est pour ça que c'est bien de s'y intéresser, de bien comprendre son principe et de prendre le temps de s'exercer parce que ce fonctionnement n'est pas très intuitif au débu (on a très souvent envie de coder directement la fonction "compliqué" car on sait les soucis que l'on peut rencontrer).

Cours de François Brucker

1. Départ

Dans ce premier chapitre on met en place le projet de développement pour s'initier à la programmation par test.

Grâce à ce chapitre on a introduit les bases de la programmation par test en mettant l'accent sur la création de tests avant l'écriture de code fonctionnel, et en décrivant un processus itératif pour développer et améliorer le code de manière incrémentale.

2. Value Object

Ce chapitre se concentre sur la mise en œuvre d'objets de valeur (value objects) dans le processus de développement.

Grâce à ce chapitre on a mis en lumière l'importance de garantir le bon fonctionnement de l'application avant de se préoccuper de son optimisation, et on a introduit la notion d'objets de valeur comme moyen de sécuriser et de rendre prévisible le comportement du code.

3. ==

Le troisième chapitre se concentre sur l'implémentation de la comparaison d'égalité (==) entre les objets Dollar.

Ce chapitre illustre la démarche itérative du TDD pour implémenter la comparaison d'égalité entre les objets Dollar, en mettant l'accent sur la détection des duplications et la résolution progressive des problèmes identifiés.

4. mul

Le quatrième chapitre se concentre sur l'implémentation de la méthode spéciale mul pour permettre la multiplication des objets Dollar.

ce chapitre illustre comment utiliser la méthodologie TDD pour implémenter la multiplication des objets Dollar en ajoutant la méthode spéciale mul. Ce processus met en évidence la progression itérative du développement logiciel en identifiant les tâches à accomplir et en les réalisant une par une.

5. Privacy

Le cinquième chapitre se concentre sur la confidentialité de l'attribut montant et sur la manière de tester les objets Dollar en se concentrant sur leur comportement externe plutôt que sur leur implémentation interne.

Ce chapitre illustre l'importance de tester le comportement externe des objets Dollar et de maintenir la confidentialité de leur implémentation interne. La méthodologie TDD est utilisée pour guider le développement de manière itérative, en se concentrant sur les fonctionnalités à implémenter et en vérifiant leur fonctionnement par le biais de tests.

6. Dollar

Dans ce chapitre, l'objectif est de commencer à introduire les Francs dans le système de gestion de la monnaie.

Ce chapitre montre comment découper des tâches ambitieuses en étapes plus gérables et comment commencer à intégrer de nouvelles fonctionnalités dans le système en utilisant la méthodologie TDD. La todo list sert de guide pour s'assurer que toutes les étapes nécessaires sont prises en compte et réalisées de manière itérative.

7. même == pour tous

Ce chapitre se concentre sur l'élimination de la duplication du code identique présent dans les classes Dollar et Franc en les faisant hériter d'une classe mère Monnaie.

Ce chapitre montre l'importance de l'élimination de la duplication du code pour maintenir un code propre et éviter les répétitions inutiles. L'utilisation de l'héritage et de la remontée des méthodes communes dans une classe mère permet d'optimiser la structure du code.

8. Franc/Dollar

Ce chapitre se concentre sur la comparaison entre les objets de classe Franc et Dollar.

Ce chapitre met en lumière l'importance de coder en tenant compte de la signification des objets plutôt que de leur implémentation interne. La todo list est mise à jour pour réfléchir à une meilleure façon de gérer la comparaison des devises.

9. Duplication Franc/Dollar

Dans ce chapitre, l'objectif est de supprimer la duplication entre les classes Franc et Dollar en les remplaçant par une seule classe, tout en garantissant que toutes les fonctionnalités sont préservées grâce aux tests.

Ce chapitre montre comment utiliser le design pattern factory pour remplacer les références directes aux classes Franc et Dollar par des fonctions de création d'objets, en préparation de leur unification.

10. Devise

Dans ce chapitre, l'objectif est de remplacer les classes Franc et Dollar par une seule classe Monnaie tout en maintenant les fonctionnalités existantes et en éliminant les duplications de code.

Ce chapitre montre comment unifier les classes Franc et Dollar en une seule classe Monnaie tout en maintenant les fonctionnalités et en éliminant les duplications de code, grâce à l'utilisation de patterns de refactoring et de bonnes pratiques de conception.

11. Unification de *

Dans ce chapitre, l'objectif était de supprimer les méthodes de multiplication __mul__ des classes Franc et Dollar pour les unifier dans la classe Monnaie.

Ce chapitre montre comment unifier les méthodes de multiplication __mul__ des classes Franc et Dollar dans la classe Monnaie, en éliminant les duplications de code et en maintenant les fonctionnalités existantes.

PS : il faut bien penser à retirer l'importation des classes Dollar et Franc qui n'existe plus du fichier des tests sinon ça marche pas :)

12. Addition 1/3

Dans cette étape, nous avons ajouté la fonctionnalité d'addition pour une unique devise.

Cette étape nous a permis d'ajouter la fonctionnalité d'addition pour une unique devise, ce qui constitue un progrès dans la réalisation des opérations monétaires de base.

Voici le test que j'ai ajouté :

def test_addition():
    cinq = monnaie.dollar(5)
    deux = monnaie.dollar(2)
    sept = monnaie.dollar(7)
    assert cinq.plus(deux) == sept

Et voici mon code :

class Monnaie:
    def __init__(self, montant, devise):
        self.montant = montant
        self.devise = devise

    def __eq__(self, other):
        if(self.devise==other.devise) :
            return self.montant == other.montant
        else :
            return False
    
    def __mul__(self, multiplicateur):
        return Monnaie(self.montant * multiplicateur, self.devise)
    
    def plus(self, other):
        return Monnaie(self.montant + other.montant, self.devise)

Et mes tests passent. Après vérification ma fonction plus est similaire à la correction et mon test peut-être simplifié de cette façon mais l'idée est la même :

def test_plus():
    assert monnaie.dollar(7) == monnaie.dollar(5).plus(monnaie.dollar(2))

13. Addition

Avant de traiter l'addition de deux monnaies différentes, il est nécessaire de commencer par traiter le cas de l'addition de montants d'une même monnaie.

Cela permet de garantir que l'addition de montants d'une même monnaie fonctionne correctement avant de passer à des cas plus complexes impliquant des devises différentes.

Après avoir traité l'addition de montants d'une même monnaie, il est nécessaire de résoudre le problème de l'addition de montants de devises différentes.

Cela permet de résoudre le problème de l'addition de montants de devises différentes en assurant une conversion appropriée des montants avant de les additionner.

Après avoir traité l'addition de montants de devises différentes, il est temps de généraliser cette approche pour toutes les opérations d'addition.

En suivant ces étapes, l'ajout de montants de devises différentes sera correctement géré par la classe Somme, tandis que les montants de la même devise resteront gérés par la classe Monnaie.

14. Conversion

Pour permettre que $5 + $2 corresponde à quelque chose qui vaut à $7, nous devons travailler sur la méthode conversion de la classe Banque. Actuellement, c'est une méthode fictive qui renvoie $7. Nous devons la rendre fonctionnelle pour gérer les conversions de devises.

L'implémentation actuelle de la méthode Banque.conversion pose des problèmes car la banque doit connaître l'implémentation de Monnaie pour accéder à l'attribut montant. Cela viole le principe de la loi de Déméter en matière de développement orienté objet.

L'étape précédente a permis de baisser le niveau de connaissance de la banque, mais il reste à traiter le cas où la méthode Banque.conversion a une Monnaie comme paramètre. Pour cela, nous devons ajouter une méthode de conversion dans la classe Monnaie.

15. Taux de change

Dans cette partie, nous nous attaquons à la gestion des taux de change. Nous commençons par travailler sur les objets de type Monnaie pour réaliser des conversions entre devises.

Maintenant que la conversion de devises pour les sommes de monnaies identiques fonctionne, nous passons à la gestion des conversions pour des devises différentes.

Pour généraliser les opérations sur les sommes et les monnaies, nous utilisons le design pattern composite. Nous devons nous assurer que les opérations telles que l'addition et la multiplication fonctionnent correctement avec les sommes et les monnaies.

Conclusion du cours

La programmation par les tests (TDD) est une approche de développement logiciel où les tests sont écrits avant le code de production. Durant ce cours on a vu son principe et ses qualités :

  1. Itératif : Les tests sont écrits, le code est implémenté pour les faire passer, puis refactoré si nécessaire, dans un cycle itératif.

  2. Qualité : Les tests automatisés identifient les erreurs tôt, garantissant la qualité du code et réduisant les coûts de maintenance.

  3. Conception dirigée par les tests : Les tests définissent le comportement attendu du logiciel, guidant ainsi sa conception et son implémentation.

  4. Documentation vivante : Les tests servent également de documentation du code, facilitant sa compréhension et favorisant la collaboration.

  5. Agilité : La TDD permet une adaptation rapide aux changements et une réactivité aux besoins métier, assurant un développement fiable et évolutif.

La programmation par les tests est essentielle pour développer rapidement des logiciels de haute qualité, tout en favorisant la collaboration et l'agilité.

Exercices

Maintenant que je connais mieux le fonctionnement du TDD et que j'ai réalisé mon premier programme à l'aide du cours, je peux tenter de réaliser un exercice par moi-même. J'ai donc demandé à ChatGPT de me proposer un exercice pour mettre en application ce que j'ai appris :

Donc on va coder une calculatrice simple !

Calculatrice simple

Je crée un nouveau dossier calculatrice-tdd dans lequel j'ajoute un fichier pour les tests test_calculatrice.py, un fichier pour mon code calculatrice.py et bien sur on n'oublies pas todo.md.

La première chose c'est donc de remplir notre todo. Le plus instinctif ça serait d'écrire ça :

## MY TODO 1
- [] Pouvoir ajouter deux nombres
- [] Pouvoir soustraire deux nombres
- [] Pouvoir multiplier deux nombres
- [] Pouvoir diviser deux nombres

Commençons par l'addition. On écrit un test simple :

test_calculatrice.py

from calculatrice import Calculatrice

def test_addition():
    assert Calculatrice.add(1,2)==3

Je lance mon test :

python -m pytest test_calculatrice.py

Et évidemment ça échoue, il n'existe pas de class Calculatrice. On en crée donc une mais qui ne fait rien :

calculatrice.py

class Calculatrice():
    pass

Maintenant l'erreur vient du fais que la class Calculatrice n'a pas de méthode add. On va donc la coder :

class Calculatrice():
    def add(self, other) :
        return self + other

Et le test passe ! Maintenant on va s'ocupper de la soustraction. J'écris un nouveau test :

test_calculatrice.py

from calculatrice import Calculatrice

def test_addition():
    assert Calculatrice.add(1,2)==3

def test_soustraction():
    assert Calculatrice.sub(5,3)==2

Evidemment le test échoue car on a pas créé de méthode sub. On s'occupe de le faire :

class Calculatrice():
    def add(self, other) :
        return self + other
    
    def sub(self, other) :
        return self - other

Super les tests passent !

On va faire la multiplication.

test_calculatrice.py

from calculatrice import Calculatrice

def test_addition():
    assert Calculatrice.add(1,2)==3

def test_soustraction():
    assert Calculatrice.sub(5,3)==2

def test_multiplication():
    assert Calculatrice.mul(4,5)==20

calculatrice.py

class Calculatrice():
    def add(self, other) :
        return self + other
    
    def sub(self, other) :
        return self - other
    
    def mul(self, other) : 
        return self*other

Et les 3 tests passent bien.

La division pour finir :

test_calculatrice.py

from calculatrice import Calculatrice

def test_addition():
    assert Calculatrice.add(1,2)==3

def test_soustraction():
    assert Calculatrice.sub(5,3)==2

def test_multiplication():
    assert Calculatrice.mul(4,5)==20

def test_division():
    assert Calculatrice.div(36,3)==12

calculatrice.py

class Calculatrice():
    def add(self, other) :
        return self + other
    
    def sub(self, other) :
        return self - other
    
    def mul(self, other) : 
        return self*other
    
    def div(self, other):
        return self/other

Et nos tests passent bien.

Bon cet exercice est assez simple parce qu'on manipule des nombres, même si je peux complexifier le code j'ai envie d'aller vers autre chose de moins intuitif.

Gestion d'une bibliothèque

Je redemande à ChatGPT un nouvel exo mais un peut moins simple cette fois :

Je crée mes différents fichiers et je rempli ma todo.

## MY TODO
- [] Pouvoir ajouter un livre avec titre auteur année de publication et statut
- [] Rechercher un livre par titre ou par auteur
- [] Emprunter un livre disponible
- [] Retourné un livre emprunté

On va commencer par ajouter un livre car sans ça on ne peut pas faire les autes.

Ajouter un livre

Je ne savais pas trop quoi mettre comme test pour vérifier que l'ajout a bien fonctionné. J'ai fait ça sans être trop sûre de moi :

test_bibliotheque.py

from bibliotheque import Bibliotheque

def test_ajout_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.livre_existe("L'Ecume des jours")

Evidemment le test échoue, il n'existe pas de class Bibliotheque et encore moins de méthode add_book ou livre_existe.

Je commence mon code dans bibliotheque.py. Evidemment ça a pas été évident du premier coup, j'ai un peu eu du mal au début (surtout que je code pas souvent en orienté objet...) mais au final j'ai réussi à m'en sortir ! Voici le code :

class Bibliotheque() : 
    def __init__(self):
        self.livres = []

    def add_book(self, titre, auteur, annee):
        livre = {"titre" : titre, "auteur": auteur, "année" : annee, "disponible" : True}
        self.livres.append(livre)

    def livre_est_disponible(self, titre):
        for livre in self.livres : 
            if livre["titre"] == titre:
                return livre["disponible"]
        return False

Et ça passe ! On peut mettre à jour la Todo.

## MY TODO
- [x] Pouvoir ajouter un livre avec titre auteur année de publication et statut
- [-] Rechercher un livre par titre et obtenir ses infos
- [ ] Rechercher un livre par auteur et avoir la liste de ses livres
- [] Emprunter un livre disponible
- [] Retourné un livre emprunté

On va s'occuper de la recherche d'un livre maintenant, mais comme on a déjà codé le fait qu'il existe on va dire que cette fois on veut savoir ses informations et surtout sa disponibilité.

Recherche d'un livre par titre

On commence par écrire le test associé à la fonctionnalité :

test_bibliotheque.py

from bibliotheque import Bibliotheque

def test_ajout_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.livre_existe("L'Ecume des jours")

def test_recherche_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.recherche_livre("L'Ecume des jours")==["L'Ecume des jours","Boris Vian",1947,True]

Le test échoue, on va rajouter une nouvelle méthode recherche_livre.

class Bibliotheque() : 
    def __init__(self):
        self.livres = []

    def add_book(self, titre, auteur, annee):
        livre = {"titre" : titre, "auteur": auteur, "année" : annee, "disponible" : True}
        self.livres.append(livre)

    def livre_existe(self, titre):
        for livre in self.livres : 
            if livre["titre"] == titre:
                return True
        return False
    
    def recherche_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre:
                return [livre["titre"],livre["auteur"],livre["année"],livre["disponible"]]

Et ça passe du premier coup ! On peut mettre à jour la Todo.

## MY TODO
- [x] Pouvoir ajouter un livre avec titre auteur année de publication et statut
- [x] Rechercher un livre par titre et obtenir ses infos 
- [-] Rechercher un livre par auteur et obtenir la liste de ses livres 
- [] Emprunter un livre disponible
- [] Retourné un livre emprunté

Recherche d'un livre par auteur

On commence par écrire le test associé à la fonctionnalité :

test_bibliotheque.py

from bibliotheque import Bibliotheque

def test_ajout_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.livre_existe("L'Ecume des jours")

def test_recherche_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.recherche_livre("L'Ecume des jours")==["L'Ecume des jours","Boris Vian",1947,True]

def test_recherche_livres_par_auteur():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    bibliotheque.add_book("L'Arrache-coeur","Boris Vian",1953)
    bibliotheque.add_book("Les Misérables","Victor Hugo",1862)
    assert bibliotheque.recherche_livres_par_auteur("Boris Vian")==["L'Ecume des jours","L'Arrache-coeur"]
    assert bibliotheque.recherche_livres_par_auteur("Victor Hugo")==["Les Misérables"]

Je commence donc à coder une fonction pour faire passer le test : bibliotheque.py

class Bibliotheque() : 
    def __init__(self):
        self.livres = []

    def add_book(self, titre, auteur, annee):
        livre = {"titre" : titre, "auteur": auteur, "année" : annee, "disponible" : True}
        self.livres.append(livre)

    def livre_existe(self, titre):
        for livre in self.livres : 
            if livre["titre"] == titre:
                return True
        return False
    
    def recherche_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre:
                return [livre["titre"],livre["auteur"],livre["année"],livre["disponible"]]
            
    def recherche_livres_par_auteur(self, auteur):
        listes_des_livres=[]
        for livre in self.livres : 
            if livre["auteur"]==auteur:
                listes_des_livres.append(livre["titre"])
        return listes_des_livres

Ca passe aussi ! On peut mettre à jour la Todo.

## MY TODO
- [x] Pouvoir ajouter un livre avec titre auteur année de publication et statut
- [x] Rechercher un livre par titre et obtenir ses infos 
- [x] Rechercher un livre par auteur et obtenir la liste de ses livres 
- [-] Emprunter un livre disponible
- [] Retourné un livre emprunté

Emprunter un livre

On va s'occuper maintenant de l'emprunt d'un livre si il est disponible.

Test associé

bibliotheque.py

from bibliotheque import Bibliotheque

def test_ajout_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.livre_existe("L'Ecume des jours")

def test_recherche_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.recherche_livre("L'Ecume des jours")==["L'Ecume des jours","Boris Vian",1947,True]

def test_recherche_livres_par_auteur():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    bibliotheque.add_book("L'Arrache-coeur","Boris Vian",1953)
    bibliotheque.add_book("Les Misérables","Victor Hugo",1862)
    assert bibliotheque.recherche_livres_par_auteur("Boris Vian")==["L'Ecume des jours","L'Arrache-coeur"]
    assert bibliotheque.recherche_livres_par_auteur("Victor Hugo")==["Les Misérables"]

def test_emprunt_livre():
    bibliotheque=Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    bibliotheque.add_book("L'Arrache-coeur","Boris Vian",1953)
    bibliotheque.add_book("Les Misérables","Victor Hugo",1862)
    assert bibliotheque.emprunt_livre("L'Arrache-coeur")==True
    assert bibliotheque.emprunt_livre("L'Arrache-coeur")==False
    assert bibliotheque.emprunt_livre("L'Ecume des jours")==True

Code associé

bibliotheque.py

class Bibliotheque() : 
    def __init__(self):
        self.livres = []

    def add_book(self, titre, auteur, annee):
        livre = {"titre" : titre, "auteur": auteur, "année" : annee, "disponible" : True}
        self.livres.append(livre)

    def livre_existe(self, titre):
        for livre in self.livres : 
            if livre["titre"] == titre:
                return True
        return False
    
    def recherche_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre:
                return [livre["titre"],livre["auteur"],livre["année"],livre["disponible"]]
            
    def recherche_livres_par_auteur(self, auteur):
        listes_des_livres=[]
        for livre in self.livres : 
            if livre["auteur"]==auteur:
                listes_des_livres.append(livre["titre"])
        return listes_des_livres

    def emprunt_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre and livre["disponible"]:
                livre["disponible"]=False
                return True
        return False

On peut mettre à jour la Todo.

## MY TODO
- [x] Pouvoir ajouter un livre avec titre auteur année de publication et statut
- [x] Rechercher un livre par titre et obtenir ses infos 
- [x] Rechercher un livre par auteur et obtenir la liste de ses livres 
- [x] Emprunter un livre disponible
- [-] Retourné un livre emprunté

Retourner un livre

On va s'occuper maintenant de retourner un livre emprunté.

Test associé

bibliotheque.py

from bibliotheque import Bibliotheque

def test_ajout_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.livre_existe("L'Ecume des jours")

def test_recherche_livre():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    assert bibliotheque.recherche_livre("L'Ecume des jours")==["L'Ecume des jours","Boris Vian",1947,True]

def test_recherche_livres_par_auteur():
    bibliotheque= Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    bibliotheque.add_book("L'Arrache-coeur","Boris Vian",1953)
    bibliotheque.add_book("Les Misérables","Victor Hugo",1862)
    assert bibliotheque.recherche_livres_par_auteur("Boris Vian")==["L'Ecume des jours","L'Arrache-coeur"]
    assert bibliotheque.recherche_livres_par_auteur("Victor Hugo")==["Les Misérables"]

def test_emprunt_livre():
    bibliotheque=Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    bibliotheque.add_book("L'Arrache-coeur","Boris Vian",1953)
    bibliotheque.add_book("Les Misérables","Victor Hugo",1862)
    assert bibliotheque.emprunt_livre("L'Arrache-coeur")==True
    assert bibliotheque.emprunt_livre("L'Arrache-coeur")==False
    assert bibliotheque.emprunt_livre("L'Ecume des jours")==True

def test_retour_livre():
    bibliotheque=Bibliotheque()
    bibliotheque.add_book("L'Ecume des jours","Boris Vian",1947)
    bibliotheque.add_book("L'Arrache-coeur","Boris Vian",1953)
    bibliotheque.add_book("Les Misérables","Victor Hugo",1862)
    bibliotheque.emprunt_livre("L'Arrache-coeur")
    bibliotheque.emprunt_livre("L'Ecume des jours")
    bibliotheque.retour_livre("L'Arrache-coeur")
    assert bibliotheque.livre_disponible("L'Arrache-coeur")==True
    assert bibliotheque.livre_disponible("L'Ecume des jours")==False

Code associé

bibliotheque.py

class Bibliotheque() : 
    def __init__(self):
        self.livres = []

    def add_book(self, titre, auteur, annee):
        livre = {"titre" : titre, "auteur": auteur, "année" : annee, "disponible" : True}
        self.livres.append(livre)

    def livre_existe(self, titre):
        for livre in self.livres : 
            if livre["titre"] == titre:
                return True
        return False
    
    def recherche_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre:
                return [livre["titre"],livre["auteur"],livre["année"],livre["disponible"]]
            
    def recherche_livres_par_auteur(self, auteur):
        listes_des_livres=[]
        for livre in self.livres : 
            if livre["auteur"]==auteur:
                listes_des_livres.append(livre["titre"])
        return listes_des_livres

    def emprunt_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre and livre["disponible"]:
                livre["disponible"]=False
                return True
        return False

    def retour_livre(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre:
                livre["disponible"]=True
        pass
    
    def livre_disponible(self, titre):
        for livre in self.livres :
            if livre["titre"] == titre: 
                return livre["disponible"]
        return False

On peut mettre à jour la Todo.

## MY TODO
- [x] Pouvoir ajouter un livre avec titre auteur année de publication et statut
- [x] Rechercher un livre par titre et obtenir ses infos 
- [x] Rechercher un livre par auteur et obtenir la liste de ses livres 
- [x] Emprunter un livre disponible
- [x] Retourné un livre emprunté

On a fini notre gestionaire de bibliothèque !

Sytème de réservation de vol

Je redemande à ChatGPT un nouvel exo mais encore plus dur :

Je crée mes différents fichiers et je rempli ma todo.

## MY TODO
- [] Pouvoir ajouter un nouveau vol en précisant la compagnie, la ville de départ, la ville d'arrivée, l'heure de départ, l'heure d'arrivée, la date du vol et le numéro de vol
- [] Rechercher des vols disponibles en précisant une ville de départ, une ville d'arrivée et la date du voyage
- [] Pouvoir réserver un vol et obtenir un numéro de réservation
- [] Pouvoir réserver un siège sur le vol à l'aide du numéro de réservation
- [] Pouvoir annuler une réservation à l'aide de son numéro

Ajouter un nouveau vol

test_vol.py

from vol import VolSysteme

def test_ajouter_vol():
    volsysteme=VolSysteme()
    volsysteme.ajouter_vol("Rayanair","Marseille","Lisbonne","16:00","17:20","12/03/2024","RYA2598")
    volsysteme.ajouter_vol("EasyJet","Marseille","Lisbonne","15:10","16:35","12/03/2024","EZS081")
    assert volsysteme.recherche_vol("Marseille","Lisbonne","12/03/2024")==["RYA2598","EZS081"]

Evidemment le test échoue, il n'existe pas de class VolSysteme et encore moins de méthode ajouter_vol ou recherche_vol.

Je commence mon code dans vol.py.

class VolSysteme():
    def __init__(self):
        self.vols = []

    def ajouter_vol(self,compagnie, depart, arrivee, heured, heurea, date, numero):
        vol = {"compagnie":compagnie, "depart":depart, "arrivee":arrivee, "heured":heured, "heurea":heurea, "date": date, "numero":numero}
        self.vols.append(vol)
    
    def recherche_vol(self,depart,arrivee,date):
        vols_possibles=[]
        for vol in self.vols:
            print(vol)
            if(vol["arrivee"] == arrivee and vol["depart"] == depart and vol["date"] == date):
                vols_possibles.append(vol["numero"])
        return vols_possibles
## MY TODO
- [x] Pouvoir ajouter un nouveau vol en précisant la compagnie, la ville de départ, la ville d'arrivée, l'heure de départ, l'heure d'arrivée, la date du vol et le numéro de vol
- [x] Rechercher des vols disponibles en précisant une ville de départ, une ville d'arrivée et la date du voyage
- [] Pouvoir réserver un vol et obtenir un numéro de réservation
- [] Pouvoir réserver un siège sur le vol à l'aide du numéro de réservation
- [] Pouvoir annuler une réservation à l'aide de son numéro

Suite de l'exercice

J'ai continué l'exercice de la même manière. Voici mes deux fichiers finaux :

test_vol.py

from vol import VolSysteme

def test_ajouter_vol():
    volsysteme=VolSysteme()
    volsysteme.ajouter_vol("Rayanair","Marseille","Lisbonne","16:00","17:20","12/03/2024","RYA2598")
    volsysteme.ajouter_vol("EasyJet","Marseille","Lisbonne","15:10","16:35","12/03/2024","EZS081")
    assert volsysteme.recherche_vol("Marseille","Lisbonne","12/03/2024")==["RYA2598","EZS081"]

def test_réserver_siège():
    volsysteme=VolSysteme()
    volsysteme.ajouter_vol("Rayanair","Marseille","Lisbonne","16:00","17:20","12/03/2024","RYA2598")
    volsysteme.ajouter_vol("EasyJet","Marseille","Lisbonne","15:10","16:35","12/03/2024","EZS081")
    assert volsysteme.réserver_siège("RYA2598", "A1")==True
    assert ("A1" in volsysteme.vols[0]["sieges"]) == True

def test_annuler_réservation():
    volsysteme=VolSysteme()
    volsysteme.ajouter_vol("Rayanair","Marseille","Lisbonne","16:00","17:20","12/03/2024","RYA2598")
    volsysteme.ajouter_vol("EasyJet","Marseille","Lisbonne","15:10","16:35","12/03/2024","EZS081")
    assert volsysteme.réserver_siège("RYA2598", "A1")==True
    assert volsysteme.annuler_réservation("RYA2598", "A1")==True
    assert ("A1" not in volsysteme.vols[0]["sieges"]) == True

vol.py

class VolSysteme():
    def __init__(self):
        self.vols = []

    def ajouter_vol(self,compagnie, depart, arrivee, heured, heurea, date, numero):
        vol = {"compagnie":compagnie, "depart":depart, "arrivee":arrivee, "heured":heured, "heurea":heurea, "date": date, "numero":numero}
        self.vols.append(vol)
    
    def recherche_vol(self,depart,arrivee,date):
        vols_possibles=[]
        for vol in self.vols:
            print(vol)
            if(vol["arrivee"] == arrivee and vol["depart"] == depart and vol["date"] == date):
                vols_possibles.append(vol["numero"])
        return vols_possibles
    
    def réserver_siège(self, numéro_vol, siège):
        for vol in self.vols:
            if vol["numero"] == numéro_vol:
                if "sieges" not in vol:
                    vol["sieges"] = []
                vol["sieges"].append(siège)
                return True
        return False

    def annuler_réservation(self, numéro_vol, siège):
        for vol in self.vols:
            if vol["numero"] == numéro_vol and "sieges" in vol and siège in vol["sieges"]:
                vol["sieges"].remove(siège)
                return True
        return False

Maintenant je me dis qu'on peut ajouter une gestion des passagers et de les tarifs. Je fais donc une nouvelle Todo, créer mes tests et ajuster le code en conséquence.

Ici, vous trouverez mes différents fichiers sur lesquels j'ai travaillé.

Horodateur

Date Heures passées Indications
Samedi 24/02 2H Environnement + Parie 1/3
Samedi 24/02 1H30 Partie 2/3
Dimanche 25/02 2H Partie 3/3
Mercredi 28/02 30min Exercice calculatrice
Mercredi 28/02 2H Exercice bibliothèque
Vendredi 01/03 2H Exercice réservation de vol