Assert et tests

Introduction

Écrire un programme, c’est bien. Écrire un programme juste, c’est mieux. Nous verrons, ici, comment limiter les erreurs en testant des bouts de code que l’on écrit sur des cas particuliers pour lesquels on connait bien le résultat attendu. Il existe plusieurs méthodes, nous en verrons deux, assert et doctest.


Développement piloté par les tests

Le développement piloté par les tests ou TDD (pour Test Driven Development) est une méthode d’écriture de programme qui met en avant le fait d’écrire d’abord un test pour chaque spécification du programme puis écrire le code qui permettra au programme de passer ce test avec succès. Cette manière de procéder permet en général de penser en premier lieu aux spécifications voulues, ce qui améliore la structure générale du code produit et permet de s’assurer que l’on dispose de toute une batterie de tests sous la main . Plus particulièrement, le TDD a été théorisé sous forme de trois lois.

  • On doit écrire un test qui échoue avant de pouvoir écrire le code permettant de le faire réussir.
  • Il ne faut tester qu’un point précis du programme sous forme d’une assertion unique 4.
  • Il ne faut écrire que le minimum de code permettant de réussir le test.

  • Les assertions

    Les assertions sont un moyen simple de s'assurer, avant de continuer, qu'une condition est respectée. En général, on les utilise dans des blocstry … except. Voyons comment cela fonctionne : nous allons pour l'occasion découvrir un nouveau mot-clé (encore un),assert. Sa syntaxe est la suivante :

    assert test                     
                                    

    Si le test renvoieTrue, l'exécution se poursuit normalement. Sinon, une exceptionAssertionErrorest levée.


    Voyons un exemple :

    
    var = 15
    assert var == 15
    assert var == 44
    
                                    

    Comme vous le voyez, la ligne 2 s'exécute sans problème et ne lève aucune exception. On teste en effet si var == 15. C'est le cas, le test est donc vrai, aucune exception n'est levée. À la ligne suivante, cependant, le test est var == 44. Cette fois, le test est faux et une exception du typeAssertionErrorest levée (regardez dans la console:-).


    Mais à quoi ça sert?

    Dans le programme testant si une année est bissextile, on pourrait vouloir s'assurer que l'utilisateur ne saisit pas une année inférieure ou égale à 0 par exemple. Avec les assertions, c'est très facile à faire :

    
    annee = input("Saisissez une année supérieure à 0 :")
    try:
        annee = int(annee) # Conversion de l'année
        assert annee > 0
    except ValueError:
         print("Vous n'avez pas saisi un nombre.")
    except AssertionError:
         print("L'année saisie est inférieure ou égale à 0.")
                            
                                    

    L’inconvénient majeur de cette méthode (même si cela peut en fait être un avantage) est que l’on doit traiter une assertion après l’autre car le programme s’arrête dès le premier test qui échoue. En cas de réussite, il ne se passe simplement rien.


    Tests dans la docstring : le module doctest

    Le module doctest permet d’inclure les tests dans la docstring descriptive de la fonction écrite définie par """. On présente dans la docstring, en plus des explications d’usage, des exemples d’utilisation de la fonction tels qu’ils pourraient être tapés directement dans la console Python. Le module doctest (via l’appel à doctest.testmod()) reconnaît les bouts de code correspondant à ces exemples et les exécute pour les comparer à la sortie demandée. On peut alors commenter in situ les tests et leurs raison d’être et avoir une sortie détaillée et globale des tests qui ont réussi ou raté.

    
    def fact(n):
        """
            paramètre n : (int) un entier
            valeur renvoyée : (int) la factorielle de n.
                
        CU : n >= 0
                
        Exemples :
                
        >>> fact(3)
        6
        >>> fact(5)
        120
        """
        res = 1
        for i in range(2, n + 1):
            res = res * i
        return res

    Cette documentation peut être exploitée avec la fonction help :

    
    help(fact)                        
                                    

    Utiliser le module doctest

    Les exemples donnés dans une chaîne de documentation peuvent être testés à l’aide d’un module de Python nommé doctest.

    Depuis la console, dans lequel la fonction fact ci-dessus est supposée chargée, tapez les deux lignes suivante:

  • import doctest ( pour importer le module)
  • doctest.testmod()
  • Svous devriez obtenir :

    
    TestResults(failed=0, attempted=2)                        
                                    

    La fonction testmod du module doctest est allée chercher dans les docstring des fonctions du module actuellement chargé, c’est-à-dire exples_doctest, tous les exemples (reconnaissables à la présence des triples chevrons >>>), et a vérifié que la fonction documentée satisfait bien ces exemples. Dans le cas présent, une seule fonction dont la documentation contient deux exemples (attempted=2) a été testée, et il n’y a eu aucun échec (failed=0). Essayez de remplacer 120 par une autre valeur puis testez


    Rendre automatique les tests

    Il est très facile de rendre automatique les tests et ainsi de ne plus avoir à faire appel explicitement (et manuellement) à la fonction testmod. Il suffit pour cela d’inclure en fin de fichier les trois lignes :

    
    if __name__ == '__main__':
    import doctest
    doctest.testmod()                        
                                    

    Testez dans le cas avec erreur et sans erreur. Nous constatons que lorsque le programme ne comporte pas d'erreur, rien de se passe. Pour vérifier que tout va bien, il suffit d'ajouter:

    
    doctest.testmod(verbose = True)           
                                    


    Conclusion

    Même si l’écriture de tests ne garantit en rien qu’un programme est correct, le fait de réfléchir aux cas limites que l’on peut rencontrer et en écrire une vérification explicite permet d’assainir l’environnement de développement. En particulier dans le cadre d’une application réelle où les demandes sont souvent amenées à être plus spécifiques à mesure que le temps avance, avoir une batterie de tests sous la main permet d’étendre le code existant sans avoir (trop) peur que la modification ne casse complètement ce qui existait déjà. Tout du moins, on s’en rend compte rapidement.

    Mettre en application

    tester le code ci-dessous et insérer des tests en utilisant les deux méthodes (insert et doctest) à vous d'imaginer les erreurs possibles:-)

    
    def moyenne():
        """fonction permettant de calculer ma moyenne trimestrielle"""  
        arret=""
        somme=0
        cpt=0
        while arret!= "stop":
            note=int(input ("entrer votre note"))
            somme=somme+note
            cpt=cpt+1
            arret=input("Si vous souhaitez stopper la saisie, ecrivez stop")
        moy=somme/cpt
        return moy