Exercice

Effectué

Dans cet exercice, vous allez utiliser pytest avec la paramétrisation pour tester une fonction. Ensuite, vous mettez à jour une classe de test pour utiliser une fixture à la place d’une méthode setup() et teardown(). L’utilisation de la paramétrisation et de fixtures vous permet de devenir plus flexible lors de la création ou de la mise à jour de tests.

Étape 1 - Ajouter un fichier avec des tests pour cet exercice

  1. Créez un fichier de test appelé test_advanced.py et ajoutez le code suivant :

    def str_to_bool(string):
        if string.lower() in ['yes', 'y', '1']:
            return True
        elif string.lower() in ['no', 'n', '0']:
            return False
    
    

    La fonction str_to_bool() accepte une chaîne comme entrée et, en fonction de son contenu, retourne une valeur True ou False.

  2. Dans le même fichier, ajoutez les tests pour la fonction str_to_bool(). Utilisez pytest.mark.parametrize() pour tester d’abord toutes les valeurs true :

    import pytest 
    
    @pytest.mark.parametrize("string", ['Y', 'y', '1', 'YES'])
    def test_str_to_bool_true(string):
        assert str_to_bool(string) is True
    
  3. Ensuite, ajoutez un autre test avec les valeurs false :

    @pytest.mark.parametrize("string", ['N', 'n', '0', 'NO'])
    def test_str_to_bool_false(string):
        assert str_to_bool(string) is False
    

    Il existe maintenant deux tests qui couvrent toutes les entrées possibles pour les valeurs de retour True et False.

Remarque

Il n’est pas courant d’avoir des tests dans le même fichier que le vrai code. Pour faire simple, les exemples de cet exercice ont du vrai code dans le même fichier. Dans les vrais projets Python, vous constaterez que les tests sont séparés par des fichiers et des répertoires du code testé.

Étape 2 - Exécuter les tests et explorer le rapport

Après avoir ajouté les tests, l’étape suivante consiste à exécuter pytest et à inspecter la sortie. Utilisez l’indicateur de détail accru (-v) afin de pouvoir voir toutes les valeurs d’entrée traitées comme un test distinct.

$ pytest -v test_avanced.py
============================= test session starts ==============================
Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 
rootdir: /private
collected 8 items

test_advanced.py::test_str_to_bool_true[Y] PASSED                        [ 12%]
test_advanced.py::test_str_to_bool_true[y] PASSED                        [ 25%]
test_advanced.py::test_str_to_bool_true[1] PASSED                        [ 37%]
test_advanced.py::test_str_to_bool_true[YES] PASSED                      [ 50%]
test_advanced.py::test_str_to_bool_false[N] PASSED                       [ 62%]
test_advanced.py::test_str_to_bool_false[n] PASSED                       [ 75%]
test_advanced.py::test_str_to_bool_false[0] PASSED                       [ 87%]
test_advanced.py::test_str_to_bool_false[NO] PASSED                      [100%]

============================== 8 passed in 0.01s ===============================

Même si vous n’avez écrit que deux fonctions de test, pytest a pu créer huit tests au total grâce à la fonction parametrize().

Étape 3 - Porter un test existant sur une fixture

  1. Ajoutez un nouveau test basé sur une classe au fichier test_advanced.py. Ce test doit utiliser une fonction setup() et teardown() qui crée un fichier temporaire avec du texte dedans. Après chaque test, le fichier est supprimé. Il doit se présenter comme suit :

    import os
    
    
    class TestFile:
    
        def setup(self):
            with open("/tmp/done", 'w') as _f:
                _f.write("1")
    
        def teardown(self):
            try:
                os.remove("/tmp/done")
            except OSError:
                pass
    
        def test_done_file(self):
            with open("/tmp/done") as _f:
                contents = _f.read()
            assert contents == "1"
    

    Cette classe de test crée un fichier, mais est problématique, car le chemin /tmp/ n’est pas garanti d’être présent sur chaque système.

  2. Créez un fixture qui utilise le fixture pytesttmpdir() pour écrire dans le fichier et renvoyer le chemin d’accès :

    import pytest
    
    @pytest.fixture
    def tmpfile(tmpdir):
        def write():
            file = tmpdir.join("done")
            file.write("1")
            return file.strpath
        return write
    

    La fixture tmpfile() utilise la fixture tmpdir() de pytest, ce qui garantit un fichier temporaire valide qui est nettoyé une fois les tests effectués.

  3. Mettez à jour la classe TestFile afin qu’elle utilise la fixture au lieu des méthodes d’assistance :

    class TestFile:
    
        def test_f(self, tmpfile):
            path = tmpfile()
            with open(path) as _f:
                contents = _f.read()
            assert contents == "1"
    

    Cette classe de test peut maintenant s’assurer qu’un fichier temporaire est créé et a le contenu approprié pour que l’assertion fonctionne.

Vérifier votre travail

À présent, vous devriez avoir un fichier Python appelé test_advanced.py avec le code suivant :

  • Une fonction str_to_bool() qui accepte une chaîne et retourne une valeur booléenne en fonction du contenu de la chaîne.
  • Deux tests paramétrisés pour la fonction str_to_bool(), l’un qui teste les valeurs True et l’autre qui teste les valeurs False.
  • Une fixture pytest personnalisée qui utilise la fixture tmpdir() pour créer un fichier terminé temporaire avec du contenu.
  • Une classe de test qui utilise la fixture personnalisée tmpfile() pour créer le fichier.

Tous les tests doivent être réussis quand ils sont exécutés dans le terminal, et ne présenter aucune erreur.