• 4 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours est en vidéo.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

J'ai tout compris !

Mis à jour le 07/10/2017

Décelez les éléments à tester

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

 

Commencez par télécharger le projet exemple sur Github.

C'est fait ? Parfait ! Regardons un peu à quoi il ressemble.

Il contient deux scripts écrits en Python qui se trouvent dans le dossier  program. Le premier,   download_agents.py, sert à récupérer des agents de l'API PPLAPI. Le second,   world.py, est le script principal du programme.

D'ailleurs, que fait-il ? Nous le lançons en utilisant la commande  python program/world.py program/agents-100k.json  et il va afficher deux graphiques :

  • le premier affiche l'agréabilité moyenne en fonction de la densité de population.

  • le second affiche les revenus en fonction de l'âge.

Pourquoi écrire des tests ? 

Régis a téléchargé ce même fichier et a voulu le modifier mais, évidemment, il m'a écrit pour râler un peu et il a eu raison. Pourquoi ? Premièrement car mon projet n'inclut aucune documentation. Le   Readme   est presque vide et ne nous montre pas comment nous servir du programme. Nous sommes donc obligés de lire la fonction  main  pour comprendre comment le lancer.

La seconde raison est qu'il n'inclut aucun test. Nous avons déjà parlé des différentes formes de tests. Expliquons à présent en quoi cela pose problème dans le cas d'une contribution. Afin d'ajouter sa fonctionnalité, Régis va créer une pull request. Après l'avoir lue, je téléchargerai son code sur mon ordi pour tester si sa fonctionnalité s'intègre bien à la mienne.

Sans test, je vais devoir lancer mon programme et vérifier manuellement qu'il n'y a pas d'erreur. En allant rapidement, je constaterai que le graphique s'affiche bien et que je peux accepter la pull request. Mais que se passe-t-il si Régis a malencontreusement changé le calcul de la densité de population sans que je ne m'en rende compte ? Le calcul est faux mais, étant donné que je vais vite et que je vérifie, à la main, uniquement si le graphique s'affiche, je vais laisser passer une erreur importante.

C'est la vie, me direz-vous. En effet, mais c'est bien dommage d'être si fataliste alors qu'une erreur ainsi aurait très facilement pu être évitée. Et si vous vous appelez Facebook, pensez aux éventuelles répercussions !

Commençons donc tout de suite par faire ce que j'aurais dû faire avant : ajouter des tests.

Que tester ?

La première question à se poser lorsque l'on doit ajouter des tests à un projet est : que souhaitons-nous tester ?

Est-ce le résultat de notre programme ? Dans notre cas, la production de deux graphiques. Ou est-ce l'intégralité du code, fonction par fonction ?

Une première stratégie

Tester uniquement la dernière étape de notre programme n'est pas tout à fait une bonne idée car c'est trop vaste. Adoptons donc le point de vue inverse et créons des tests unitaires qui vont vérifier que chaque fonction de notre code produit bien le résultat que nous souhaitons. Au moins nous sommes sûrs que notre programme sera parfaitement sécurisé !

Notre code contient 32 fonctions et variables de classes que nous pourrions tester. La première stratégie serait donc de créer autant de test que de fonction....

Mais faire cela est loin d'être idéal. Réfléchissons deux minutes. Pourquoi voulons-nous écrire des tests ? Pour ajouter une fonctionnalité après et être donc certains que cette dernière ne va pas avoir un effet pervers sur les anciennes.

Cela ne sert donc à rien de tester l'implémentation d'un programme, c'est à dire tout le détail. Pourquoi ? Car si je dois modifier mon code pour l'améliorer, cela va casser plusieurs tests alors même que mon programme fonctionne encore. Simplement car j'aurai écrit plusieurs tests qui dépendent de mon implémentation.

Rappels sur l'Objet

Afin de comprendre ce qu'il est préférable de tester, revenons un peu en arrière sur des notions de programmation orientée objet.

Chaque objet peut être vu comme une navette spatiale en orbite. Cette dernière n'a aucune idée de ce qui se passe dans les autres navettes. Elle sait uniquement ce qu'elle a besoin de savoir pour son bon fonctionnement à elle.

La communication avec les autres navettes se fait via des messages entrants et des messages sortants. Elle peut également envoyer des messages internes au personnel de la navette.

Tester une interface

Une bonne pratique consiste à tester uniquement les messages entrants, c'est à dire les méthodes publiques, et non les méthodes privées. En effet, nous considérons qu'un objet est une boite noire qui contient tout ce dont il a besoin pour son bon fonctionnement. Nous, externes, n'avons pas à connaitre la manière dont il nous envoie les informations. Nous vérifions simplement que ces informations correspondent à celles que nous attendons.

Ceci veut dire que nous allons tester l'interface d'un objet et non son fonctionnement interne. Ceci nous donne également une plus grande flexibilité à la fois dans nos tests et dans la configuration de nos objets. Vous pouvez ainsi facilement changer le code d'un objet sans avoir à modifier vos tests. Par exemple, si un jour vous changez la manière de calculer les données, mais que le résultat renvoyé est toujours conforme à vos attentes, le test sera toujours valide et vous n'aurez pas à le réécrire.

 

En pratique

Notre projet exécute la fonction  main()  lorsque l'utilisateur lance le programme. Nous allons donc parcourir les différentes instructions que cette dernière contient. 

def main():
    parser = argparse.ArgumentParser("Display population stats")
    parser.add_argument("src", help="Path to source json agents file")
    args = parser.parse_args()

    for agent_properties in json.load(open(args.src)):
        longitude = agent_properties.pop('longitude')
        latitude = agent_properties.pop('latitude')
        position = Position(longitude, latitude)
        zone = Zone.find_zone_that_contains(position)
        agent = Agent(position, **agent_properties)
        zone.add_inhabitant(agent)

    agreeableness_graph = AgreeablenessGraph()
    agreeableness_graph.show(Zone.ZONES)

    income_graph = IncomeGraph()
    income_graph.show(Zone.ZONES)

    agreeableness_per_age_graph = AgreeablenessPerAgeGraph()
    agreeableness_per_age_graph.show(Zone.ZONES)

Ignorons pour le moment les arguments passés en ligne de commande et concentrons-nous sur la boucle  for . 

Les objets avec lesquels nous interagissons sont  Position ,  Zone ,  Agent ,  AgreeablenessGraph et  IncomeGraph . 

Voici une première liste de ce qu'il paraît pertinent de tester :

Agent

  • modifier un attribut position

  • récupérer un attribut position

  • assigner un dictionnaire en tant qu'attributs

 

Position

  • modifier un attribut longitude_degrees

  • modifier un attribut latitude_degrees

  • modifier un attribut longitude_degrees avec une valeur supérieure à 180 renvoie une erreur.

  • modifier un attribut latitide_degrees avec une valeur supérieure à 90 renvoie une erreur.

  • récupérer une latitude

  • récupérer une longitude

 

Zone

  • trouver une zone qui contient une position

  • ajouter un habitant dans une zone

  • récupérer toutes les instances Zone (Zone.ZONES)

  • récupérer la densité de population d'une zone

  • récupérer l'agréabilité moyenne d'une zone

 

AgreeablenessGraph

  • récupérer un titre

  • récupérer x_label

  • récupérer y_label

  • récupérer xy_values sous forme de tuples

  • la première valeur de xy_values est la densité de population moyenne

  • la seconde valeur de xy_values est l'agréabilité moyenne

 

IncomeGraph

  • récupérer un titre

  • récupérer x_label

  • récupérer y_label

  • récupérer xy_values sous forme de tuples

  • la première valeur de xy_values est l'âge

  • la seconde valeur de xy_values est le revenu

 

Alors, prêts pour votre premier test ? Nous commencerons par tester très simplement qu'un agent peut bien avoir un attribut position. On se retrouve au chapitre suivant !

 

Exemple de certificat de réussite
Exemple de certificat de réussite