Chapitre 10 : Projet phase 2, les tests unitaires
Ce chapitre présente la phase 2 du projet. Il se basera sur la phase 1, donc sur l'architecture git, et le site web en Flask.
Les deux premières semaines, vous implémenterez des tests unitaires comme expliqués ci-dessous. Dans le contexte de ce projet, chaque étudiant devra faire une review du code d'un autre étudiant. Les deadlines précises sont communiquées dans les slides et sur Moodle.
Comme d'habitude, chaque groupe DOIT réaliser son travail sur son répertoire dédié sur la Forge UCLouvain qui utilise la plateforme GitLab et le gestionnaire de version Git. Ceci permettra de garder une trace des contributions des différents membres du groupe. Pensez à organiser votre Git proprement et utilisez des messages de commit clairs. Par ailleurs, toute intégration à la branche main doit se faire avec des merge requests, relues par au moins un condisciple.
Une application testée
On vous demande d'implémenter et d'intégrer à votre application web deux nouvelles pages avec diverses fonctionnalités.
On vous demande aussi d'intégrer des tests pour ces fonctionnalités.
Fonctions
Pour ce faire, vous devrez implémenter deux fonctions suivantes dans un fichier dédié emissions.py:
Distance
distance(lat_from: Decimal, long_from: Decimal, lat_to: Decimal, long_to: Decimal) -> Decimal : qui étant donné les coordonnées de l'aéroport de départ to et de celui d'arrivée from en radian retourne une distance en kilomètre. Calculer une distance entre deux points sur Terre de façon précise n'est pas si aisé. Pour nous simplifier la tâche, nous allons utiliser l'approximation suivante.
La formule à utiliser est :
D = R*(arcos(sin(lat_from)*sin(lat_to)+cos(lat_from)*cos(lat_to)*(cos(long_from-long_to))))
R est le rayon de la Terre qui est de 6378 KM. Les fonctions trigonométriques se trouvent dans la librairie math, vous pouvez les utiliser directement, en revanche, vous ne pouvez pas utiliser de librairie qui calcule pour vous la distance entre deux points sur Terre.
Attention: Dans la base de données, les angles sont donnés en dégrés et non en radiant, pour convertir les degrés en radiants, vous devez utiliser la fonction radians dans la librairie math. En Python : math.radians(degree)
Si par exemple, nous calculons la distance CRL(Brussels South Charleroi Airport)-TNG(Tangier Ibn Battouta Airport) donc la distance entre (50.46,4.45) et (35.7,-5.9) dans la base de données. En utilisant la formule, nous obtenons 1842 KM. www.airmilescalculator.com nous donne la valeur de 1838 KM pour le même vol, pas mal !
Comme la précision de cet algorithme dépend de la précision de l'arithmétique de nombres décimaux, vous devrez utiliser decimal.Decimal pour représenter les valeurs numériques.
Emission
emission(distance: Decimal, aircraft: AirCraft) -> Decimal : qui étant donné la distance ainsi que le type de l'avion vous donnerons les émissions en tonnes de CO2 du vol.
Commençons par déterminer les éléments qui nous permettront de calculer les émissions de CO2 équivalent d'un vol, pour simplifier, nous pouvons considérer qu'il existe 3 catégories d'avions qui dans la base de données seront représentés par L : light, M : medium, H Heavy et J Jumbo. Nous considérerons qu'ils consomment respectivement, 1, 2.5, 5 et 12 Tonnes de carburant/Heure.
Ensuite, pour calculer la quantité CO2 équivalent émit, nous devons multiplier la quantité de carburant par 3.15 pour obtenir la quantité de CO2 relâché dans l'atmosphère.
De plus, la combustion de carburant émet d'autres gaz en plus du CO2. Nous pouvons ajouter un facteur 2 au résultat obtenu pour avoir l'émission de CO2 équivalent.
Nous allons également considérer que la vitesse moyenne d'un avion est de 800 km/h.
Nous nous intéresserons uniquement à l'émission CO2 d'un vol, et pas à celui d'un passager. Un même avion peut avoir des configurations de sièges différentes et le taux d'occupation peut varier ce qui compliquerait davantage le calcul.
Si nous prenons un vol CRL->TNG avec une distance de 1838KM comme calculé précédemment et un avion de type H, nous pouvons faire le calcul suivant.
Durée : 2.3H (1838KM/800KM/H)
Consommation 5T/H (appareil H)
Résultat = (5*2.3*3.15*2) = 72.45
Nous avons donc 72.45 tonnes de CO2 pour tout le vol.
Voici un squelette, complétez-le en implémentant les fonctions distance et emission.
mobility/emission.py
from enum import Enum
import math
from decimal import Decimal
class AirCraft(Enum):
L = 0
M = 1
H = 2
J = 3
def distance(lat_from: Decimal, long_from: Decimal, lat_to: Decimal, long_to: Decimal) -> Decimal:
return 0
def emission(distance: Decimal, aircraft: AirCraft) -> Decimal:
return 0
Page
Vous créerez une nouvelle page "Calculateur" sur le site qui permettra de: - calculer la distance entre deux coordonnées GPS - de sélectionner deux aéroports dans des listes déroulantes et d'en afficher la distance.
Vous créerez aussi une nouvelle page "Emissions" qui permettra: - calculer les émissions de CO2 d'un vol pour lequel on donne sa distance et le type d'avion - de sélectionner deux aéroports et un modèle d'avion, et d'en afficher les émissions de CO2. - de comparer les émissions de ce vol avec d'autres émissions connues (par exemple, la quantité de CO2 émise par une voiture pour faire 100km, ou par un humain en respirant pendant une journée, les émissions de la Wallonie en un jour, ...). La comparaison apparaîtra comme un tableau trié, avec le vol classé entre la catégorie supérieure et inférieure.
La liste des aéroports et leurs coordonnées doivent être récupérées des modèles appropriés. Les données au format dictionnaire sont fournies dans la dernière version de la branche main de https://forge.uclouvain.be/linfo1002/base-project-2026, vous devez donc faire un git merge.
def get_airports():
return [
{
"iata_code": "UTK",
"name": "Utirik Airport",
"latitude_deg": 11.222219,
"longitude_deg": 169.851429,
"iso_country": "MH"
},
{
"iata_code": "OCA",
"name": "Ocean Reef Club Airport",
"latitude_deg": 25.325399398804,
"longitude_deg": -80.274803161621,
"iso_country": "US"
},
...
]
def get_airplane_classes():
return {
"L": "Light",
"M": "Medium",
"H": "Heavy",
"J": "Jumbo"
}
def get_airplanes():
return [
{
"iata_aircraft": "100",
"name": "Fokker 100",
"aircraft_type": "M"
},
...
]
Tests unitaires
On vous demande aussi de fournir une classe de tests qui testera la logique de ces différentes méthodes.
Pour ce faire, on vous demande d'utiliser le module unittest comme vu en LSINF1101 et/ou comme rappelé dans le syllabus et au cours.
Vous devrez créer vos fichiers de tests dans un sous-répertoire tests et ils devront respecter la nomenclature test_*.py où * correspond à la classe ou au module testé pour que unittest les découvre automatiquement.
Une classe de tests unitaires hérite de unittest.TestCase et porte un nom logique *TestCase correspondant à la classe ou au module testé.
Chaque classe teste une fonctionnalité précise et chaque méthode de test dans la classe sera nommée en commençant par test_ comme def test_foo(self) : contenant une série d'assertions qui vérifient que pour un input connu, l'output produit est celui attendu (expected).
Un squelette d'une telle classe ressemblera donc à ceci.
tests/test_emission.pyimport unittest import mobility.emission class EmissionTestCase (unittest.TestCase): def test_distance(self): # Un angle de 1 Radian intercept un arc de longueur égal au rayon # Nous prenons deux points distants d'un angle de 1 radian expected = 6378 a = (0, 1) b = (0, 2) actual = mobility.emission.distance(a[0], a[1], b[0], b[1]) self.assertAlmostEqual(actual, expected, 1, msg=f'My error message on pl.f({actual}) different than {expected}') def test_emission(self): # TODO # C'est à vous de jouer ici self.assertTrue(True) if __name__ == '__main__': unittest.main(verbosity=2)
Cette classe doit être enrichie et couvrir un maximum de emission (via l'utilisation de coverage). Pour exécuter vos tests, vous pouvez depuis la racine du projet lancer la commande suivante :
$ python3 -m unittest discover -v -s ./mobility/tests
Rapport
Directement sur git, vous remettrez un rapport qui répond aux critères ci-dessous.
Le rapport sera basé sur un format texte et directement fait sur Git, en Markdown (.md), en Latex, ou ReStructured text (.rst) mais ce dernier format est à éviter car il est plus difficile à écrire. Le format Latex et RST doit être également rendu au format PDF sur git. Il est important que le rapport soit fait via Git, l'historique Git pourra être utilisé pour voir les modifications apportées au rapport.
Le dépôt Git ne doit pas contenir de fichiers binaires ou le VENV. Vous devez pousser la version finale de votre code sur la branche main de votre dépôt Git. Vous devez également créer un tag phase2 pour marquer la version finale de votre code pour la phase 2.
Critères d'évaluation
Rapport :
Le rapport est clair, complet, mais concis. /1
Le rapport présente les fonctionnalités implémentées. Il inclut une capture d'écran de chaque page du site. /1
Le rapport est dans un langage correct et clair. /1
Le rapport inclus une explication de l'architecture, notamment l'utilisation du MVC. /1
Le rapport explique chacun des algorithmes, pourquoi ils sont optimaux. /1
Le rapport a été fait au format texte. /1
Site :
Le site fonctionne et est en ligne /1
Le design est réfléchi, le site est navigable, élégant, épuré /2
Le site est résistant à l'utilisateur : il n'y a pas de crash, ... /1
- Documentation du code :
Le code est commenté /1
- Test:
Le coverage est suffisant (>70 1, >80 is 2, >90 3)
Distance est testé dans plusieurs cas /2
Emission est testé dans plusieurs cas /2
Le site présente des tests d'intégration sur les routes
- Git :
L'ensemble des modifications importantes ont été faites en utilisant des merge requests. /1
Les merge requests ont fait l'objet de reviews systématiques. /1
Le git ne contient pas de fichiers binaires, le VENV ou autres fichiers inutiles. /1
- Code :
Le code est correct. /1
Le code est structuré, l'architecture MVC est respectée. /1
Les templates HTML sont propres. Les données paramétrables sont passées par l'application, et pas codées en dure. /1
- Travail de groupe :
L'évaluation du groupe se fera via un module dynamo sur Moodle, clôturé 2 jours après la soumission. Une pénalité sera appliquée pour les membres qui n'y répondent pas.
Toutes les notes de critères sont indicatives et seront utilisées pour guider la correction. Pour rappel, la phase 2 dans son ensemble compte pour 15% de la note finale. Les deadlines sont données sur Moodle. L'utilisation d'IA pour le code ou le rapport reste strictement interdite et sera sanctionnée sévèrement. Nous vous encourageons à faire des revues de code entre vous pour améliorer la qualité de votre travail. Le pair programming est autorisé en spécifiant bien "Co-authored-by: name <email>" dans les messages de commit comme le veut la convention.
Review
De plus, à la fin du rapport chaque étudiant ajoutera lui-même ou elle-même une capture d'écran, un lien, et un commentaire vers une review d'une merge request particulièrement bien fournie (au choix). Ceci compte pour 2 points de la note finale du cours et est individuel.
Correction de la review
Les critères d'évaluation de la review sont les suivants :
La pertinence de la merge request est commentée, son adéquation par rapport aux consignes est mentionnée 1
La pertinence des fichiers est discutée 1
La clarté du code (variable, commentaires) est mentionnée 1
La qualité des tests, leurs complétudes, et le coverage sont discutés 2
La review comprend au moins un commentaire positif 1
La review comprend au moins une piste d'amélioration 2
Notez que la note des critères est indicative uniquement.
Comment faire une bonne review
Lors de la peer-review du code, nous vous demandons de vérifier la qualité et la structure du code poussé par un étudiant ainsi que de la suite de test. Guidez-vous en répondant aux questions suivantes :
Cette analyse prend du temps, mais c'est important que vous appreniez à lire du code écrit par d'autres informaticiens. Dans votre vie professionnelle, vous devrez beaucoup plus souvent modifier des programmes écrits par d'autres informaticiens que de développer des programmes à partir de rien. Il est aussi important que vous appreniez à écrire des commentaires constructifs qui permettent à ceux qui les lisent d'améliorer leur travail. Évitez les formulations négatives ou blessantes. Relisez votre review en vous mettant à la place du groupe receveur et demandez-vous si elle vous semble utile et constructive.