🧪 Python et le TDD#

Important

Avant de nous lancer dans les tests unitaires en python, il convient de créer la structure de projet suivante:

Structure du projet#
.
├── dna.py
└── tests
   ├── __init__.py
   └── test_dna.py

2 directories, 3 files

Approche TDD en Python#

Nous voulons créer les fonctions suivantes:

  • Vérifier qu’une chaîne est bien un brin d’ADN valide

  • Obtenir le brin d’ADN complémentaire

  • Obtenir le nombre de codon

Indication

../../_images/dna.png

Pour rappel un brin d’ADN est constitué de 4 base azotée (A, T, G, C), organisé en codon de 3 bases. Ces bases fonctionnent par paires (d’où la structure en double hélice et c’est pour cela que l’on parle de brin d’ADN complémentaire). Ainsi A est complémentaire de T, et G est complémentaire de C.

Quelques données de test#
GCGTCC
AGGGTG
ATCTAC
ATCTACCTCTTC
CTCTTC
CTXTEC

Unittest#

La bibliothèque standard Python est livrée avec un framework de test nommé unittest, que vous pouvez utiliser pour écrire des tests automatisés pour votre code.

Le package unittest a une approche orientée objet dans laquelle les cas de test dérivent d’une classe de base, qui dispose de plusieurs méthodes utiles.

Ecriture de notre premier test#

Le package unittest définit la classe TestCase, qui est principalement conçue pour écrire des tests unitaires.

Pour commencer à écrire nos cas de test, il suffit d’importer la classe et d’en hériter.

Les méthodes de tests ont leurs noms préfixer par test. Ces méthodes testeront une unité de code donnée en utilisant différentes entrées et vérifieront les résultats attendus.

Nous allons ajouter à notre fichier test_dna.py un premier test:

import unittest


class TestDNAFunctions(unittest.TestCase):

    def test_valid_dna(self):
        self.assertEqual(is_valid_dna("GCGTCC"), True)

Nous pouvons éxécuter notre premier test pour ce faire nous allons lancer la commande suivante dans notre console:

$ python3 -m unittest

Avertissement

Attention cette commande doit être lancée à la racine de notre projet.

Nous venons de respecter les deux premières étapes du TDD:

  1. Créer un test et

  2. Vérifier que celui-ci est en erreur étant donné qu’il n’y pas de code source.

Maintenant nous allons écrire juste assez de code pour que celui-ci passe.

Dans le fichier dna.py, ajoutons le code de la fonction multiplication

def is_valid_dna(dna: str):
   return all(base in ('A', 'T', 'C', 'G', 'X') for base in dna)

Et dans le fichier test_dna.py ajoutons le code suivant:

from dna import is_valid_dna # Cette ligne permet d'importer la fonction is_valid_dna au sein de notre fichier de test

À faire

  1. Et si on relance notre test maintenant ?

  2. Pensez vous que notre jeu de test est suffisament fiable ?

  3. Et si nous ajoutions une assertion à notre méthode test_valid_dna pour vérifier que CTXTEC est invalide

  4. Que se passe t’il lors de la relance de notre test ?

  5. Pouvez-vous corriger cette erreur ?

  6. Que se passe t’il lors de la relance de notre test ?

Félicitations, vous venez de découvrir les tests unitaires et le TDD !

Les classes héritées de TestCase possédent une méthode setUp et une méthode tearDown. Elles sont respectivement exécutées avant l’appel de la méthode de test et après l’appel de la méthode de test (même si la méthode de test a levé une exception).

class ImportTestCase(unittest.TestCase):

     def setUp(self):
             # Do something before launching each test

     def tearDown(self):
             # Do something after calling each test

À faire

En respectant l’approche TDD, implémentez les deux fonctions restantes.

Indication

Pour aller plus loin je ne peux que vous recommander la lecture de la superbe documentation du module unittest.

Indication

Bien sûr la librairie de test en python est Pytest dont je vous invite à libre l’introduction sur la page dédiée à ⚗️ Flask.