Question Comment testez-vous qu'une fonction Python lève une exception?


Comment peut-on écrire un unittest qui échoue seulement si une fonction ne lance pas une exception attendue?


490
2017-09-24 20:00


origine


Réponses:


Utilisation TestCase.assertRaises (ou TestCase.failUnlessRaises) du module unittest, par exemple:

import mymod

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

428
2017-09-24 20:02



Depuis Python 2.7, vous pouvez utiliser le gestionnaire de contexte pour accéder à l'objet Exception proprement dit:

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


Dans Python 3.5, vous devez envelopper context.exception dans str, sinon vous aurez un TypeError

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

305
2017-07-02 15:16



Le code dans ma réponse précédente peut être simplifié pour:

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

Et si une fonction prend des arguments, transmettez-les simplement dans assertRaises comme ceci:

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

195
2017-09-24 20:13



Comment testez-vous qu'une fonction Python lève une exception?

Comment écrit-on un test qui échoue seulement si une fonction ne lance pas   une exception attendue?

Réponse courte:

Utilisez le self.assertRaises méthode en tant que gestionnaire de contexte:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

Manifestation

La meilleure approche pratique est assez facile à démontrer dans un shell Python.

le unittest bibliothèque

En Python 2.7 ou 3:

import unittest

En Python 2.6, vous pouvez installer un backport de 2.7 unittest bibliothèque, appelée unittest2, et juste alias que unittest:

import unittest2 as unittest

Exemples de tests

Maintenant, collez dans votre shell Python le test suivant de la sécurité de type de Python:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

Test on utilise assertRaises en tant que gestionnaire de contexte, ce qui garantit que l'erreur est correctement détectée et nettoyée lors de l'enregistrement.

Nous pourrions aussi l'écrire sans pour autant le gestionnaire de contexte, voir le test deux. Le premier argument serait le type d'erreur que vous envisagez d'élever, le second argument, la fonction que vous testez, et les args et mots-clés restants seront passés à cette fonction.

Je pense que c'est beaucoup plus simple, lisible et maintenable que de simplement utiliser le gestionnaire de contexte.

Lancer les tests

Pour exécuter les tests:

unittest.main(exit=False)

En Python 2.6, vous aurez probablement besoin de ce qui suit:

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

Et votre terminal devrait sortir ce qui suit:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

Et nous voyons cela comme prévu, en essayant d'ajouter un 1 et un '1' aboutir à un TypeError.


Pour une sortie plus verbeuse, essayez ceci:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

85
2018-01-29 19:54



Votre code doit suivre ce modèle (il s’agit d’un test de style module le plus souvent utilisé):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception as e:
       self.fail('Unexpected exception raised:', e)
    else:
       self.fail('ExpectedException not raised')

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


29
2017-09-24 20:03



de: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

Premièrement, voici la fonction correspondante (toujours dum: p) dans le fichier dum_function.py:

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

Voici le test à effectuer (seul ce test est inséré):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

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

Nous sommes maintenant prêts à tester notre fonction! Voici ce qui se passe lorsque vous essayez d'exécuter le test:

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

Le TypeError est actullay déclenché et génère un échec de test. Le problème est que c'est exactement le comportement que nous voulions: s.

Pour éviter cette erreur, exécutez simplement la fonction utilisant lambda dans l'appel de test:

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

La sortie finale:

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Parfait!

... et pour moi c'est parfait aussi !!

Merci beaucoup M. Julien Lengrand-Lambert


11
2018-02-05 17:04



j'utilise doctest[1] presque partout parce que j'aime le fait que je documente et teste mes fonctions en même temps.

Jetez un oeil à 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 mettez cet exemple dans un module et que vous l'exécutez depuis la ligne de commande, les deux cas de test sont évalués et vérifiés.

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


8
2017-09-25 11:17



Jetez un oeil à la assertRaises méthode de unittest module.


7
2017-09-24 20:01