1176 votes

Comment tester qu'une fonction Python lève une exception ?

Comment écrire un unittest qui échoue seulement si une fonction ne lève pas une exception attendue ?

963voto

Moe Points 6698

Utilice TestCase.assertRaises (o TestCase.failUnlessRaises ) du module unittest, par exemple :

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

26 votes

Y a-t-il un moyen de faire l'inverse ? Par exemple, il n'y a d'échec que si la fonction lève l'exception ?

149 votes

Notez que pour passer des arguments à myfunc vous devez les ajouter comme arguments à l'appel assertRaises. Voir la réponse de Daryl Spitzer.

2 votes

Y a-t-il un moyen d'autoriser plusieurs types d'exceptions ?

659voto

Art Points 5151

Depuis Python 2.7, vous pouvez utiliser le gestionnaire de contexte pour obtenir l'objet d'exception lancé :

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


En Python 3.5 vous devez emballer context.exception en str sinon vous obtiendrez un TypeError

self.assertTrue('This is broken' in str(context.exception))

7 votes

J'utilise Python 2.7.10, et ce qui précède ne fonctionne pas ; context.exception ne donne pas le message ; c'est un type.

8 votes

De même, dans Python 2.7 (du moins dans ma 2.7.6), l'utilisation de import unittest2 vous devez utiliser str() c'est-à-dire self.assertTrue('This is broken' in str(context.exception)) .

4 votes

Deux choses : 1. Vous pouvez utiliser assertIn au lieu de assertTrue. Par exemple, self.assertIn('This is broken', context.exception) 2. dans mon cas, avec la version 2.7.10, context.exception semble être un tableau de caractères. L'utilisation de str ne fonctionne pas. J'ai fini par faire ceci ''.join(context.exception) Donc, mis ensemble : self.assertIn('Ceci est cassé', ''.join(context.exception))

501voto

Daryl Spitzer Points 18304

Le code de ma réponse précédente peut être simplifié comme suit :

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

Et si une fonction prend des arguments, il suffit de les passer dans assertRaises comme ceci :

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

68 votes

Le deuxième extrait sur ce qu'il faut faire lorsque l'argument est passé était vraiment utile.

2 votes

J'utilise 2.7.15 . Si afunction en self.assertRaises(ExpectedException, afunction, arg1, arg2) est l'initialisateur de la classe, vous devez passer le paramètre self comme premier argument, par exemple, self.assertRaises(ExpectedException, Class, self, arg1, arg2)

1 votes

Cela fonctionne également si arg doit être de type paramètre=valeur, comme : self.assertRaises(ExpectedException, afunction, arg1=val1)

67voto

Daryl Spitzer Points 18304

Votre code devrait suivre ce modèle (c'est un test de style module unittest) :

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

Sur Python < 2.7, cette construction est utile pour vérifier des valeurs spécifiques dans l'exception attendue. La fonction unittest assertRaises vérifie uniquement si une exception a été levée.

3 votes

Et la méthode self.fail ne prend qu'un seul argument

6 votes

Cela semble trop compliqué pour tester si une fonction lève une exception. Puisque toute exception autre que celle-là entraînera l'erreur du test et que le fait de ne pas lancer d'exception fera échouer le test, il semble que la seule différence soit que si vous obtenez une exception différente avec assertRaises vous obtiendrez un ERREUR au lieu d'un ÉCHEC.

11voto

pi. Points 6026

J'utilise doctest [1] presque partout car j'aime le fait de documenter et de tester mes fonctions en même temps.

Jetez un coup d'œil à ce code :

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

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

Si vous placez cet exemple dans un module et l'exécutez à partir de la ligne de commande, les deux cas de test sont évalués et vérifiés.

[1] Documentation Python : 23.2 doctest -- Tester les exemples interactifs Python

4 votes

J'aime doctest, mais je trouve qu'il complète plutôt qu'il ne remplace unittest.

3 votes

Est-ce que doctest est moins susceptible de jouer le jeu avec le refactoring automatisé ? Je suppose qu'un outil de refactoring conçu pour python devrait faites attention aux docstrings. Quelqu'un peut-il faire des commentaires à partir de son expérience ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X