Aller au contenu

Activité - Module doctest

Dans le chapitre F01, nous avons vu qu'un docstring devait au moins présenter les spécifications de la fonction.

On peut ensuite ajouter un jeu de tests au docstring. Si ces tests sont présentés comme dans les exemples suivants, Python peut les exploiter directement grâce au module doctest.

Exemple n°1 - Tester et corriger un code

Dans la page précédente, nous avons établit un plan de test pour la fonct somme_impair(n). Cette fonction doit renvoyer la somme des n premiers entiers impairs consécutifs.

cas n valeur renvoyée attendue
aucun 0 0
un 1 1
plusieurs 4 1+3+5+7 = 16

Pour mettre en oeuvre ce plan de test, on rédige le code de la fonction, suivi du programme principal qui contient l'appel au module doctest :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def somme_impair(n):
    """
    n - int, entier strictement positif
    Sortie: int - somme des n premiers entiers impairs
    >>> somme_impair(1)
    1
    >>> somme_impair(4)
    16
    """
    s = 0
    for i in range(1, n):
        s = s + 2*i+1
    return s

##----- Mise en oeuvre des tests -----##
if __name__ == "__main__":
    import doctest
    doctest.testmod()
  1. Copiez-collez et exécutez ce code. Tous les tests sont-ils passés ?

    Une réponse

    Le module signale deux tests qui ne passent pas :

    **********************************************************************
    File "__main__", line 7, in __main__.somme_impair
    Failed example:
        somme_impair(1)
    Expected:
        1
    Got:
        0
    **********************************************************************
    File "__main__", line 9, in __main__.somme_impair
    Failed example:
        somme_impair(4)
    Expected:
        16
    Got:
        15
    **********************************************************************
    1 items had failures:
       2 of   3 in __main__.somme_impair
    ***Test Failed*** 2 failures.
    

    Il y a donc une erreur de logique dans ce code.

  2. Dans le cas où des tests ne sont pas passés, corrigez le code pour qu'il ne contienne plus d'erreur de logique.

    Une réponse

    La boucle n'est pas parcourue n fois mais seulement n-1 fois. Il faut donc modifier le paramètre de la fonction range() à la ligne 13 :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def somme_impair(n):
        """
        n - int, entier strictement positif
        Sortie: int - somme des n premiers entiers impairs
        >>> somme_impair(1)
        1
        >>> somme_impair(4)
        16
        """
        s = 0
        for i in range(n):
            s = s + 2*i+1
        return s
    
    ##----- Mise en oeuvre des tests -----##
    if __name__ == "__main__":
        import doctest
        doctest.testmod()
    

    Cette fois-ci, les tests passent : rien n'est signalé dans la console.

Exemple n°2 - Concevoir un code

On considère la fonction contient_e() qui prend en paramètre une chaîne de caractères et qui renvoie un booléen de valeur True si la chaîne contient le caractère 'e' et False sinon.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def contient_e(mot):
    """
    mot – str
    Sortie: bool – True si mot contient 'e', False sinon
    >>> ……………………………………………………………………………………
    ………………………………………
    >>> ……………………………………………………………………………………
    ………………………………………
    """

    ……………………………………………………………………………………………………………………………………………………………
    ……………………………………………………………………………………………………………………………………………………………
    ……………………………………………………………………………………………………………………………………………………………
    ……………………………………………………………………………………………………………………………………………………………
    ……………………………………………………………………………………………………………………………………………………………

##----- Mise en oeuvre des tests -----##
if __name__ == '__main__' :
    import doctest
    doctest.testmod()
  1. Complétez les lignes 5 à 8 du code ci-dessus qui contient deux tests à réaliser dans la console par le module doctest.

    Une réponse

    Il faut un test pour lequel la valeur renvoyée est True, un autre pour False :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def contient_e(mot):
        """
        mot – str
        Sortie: bool – True si mot contient 'e', False sinon
        >>> contient_e('abcd')
        False
        >>> contient_e('Exemple')
        True
        """
    
  2. Complétez la définition de la fonction contient_e() afin qu'elle respecte ses spécifications puis testez votre code.

    Un premier code possible

    On parcourt chaque caractère. Dès qu'on en trouve un qui est un 'e', on renvoie True.
    Si on les a tous parcourus et qu'on n'a trouvé aucun 'e', alors on renvoie False.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def contient_e(mot):
        """
        mot – str
        Sortie: bool – True si mot contient 'e', False sinon
        >>> contient_e('abcd')
        False
        >>> contient_e('Exemple')
        True
        """
        for carac in mot:
            if carac == 'e':
                return True
        return False
    
    
    ##----- Mise en oeuvre des tests -----##
    if __name__ == '__main__' :
        import doctest
        doctest.testmod()
    
    Un second code possible

    L'opérateur in est bien plus efficace car il répond directement à cette question :

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    def contient_e(mot):
        """
        mot – str
        Sortie: bool – True si mot contient 'e', False sinon
        >>> contient_e('abcd')
        False
        >>> contient_e('Exemple')
        True
        """
        return 'e' in mot
    
    
    ##----- Mise en oeuvre des tests -----##
    if __name__ == '__main__' :
        import doctest
        doctest.testmod()