Come si verifica se una funzione Python genera un'eccezione?


Answers

Dal momento che Python 2.7 è possibile utilizzare il gestore di contesto per ottenere una sospensione dell'oggetto effettivo Eccezione generata:

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

In Python 3.5 , è necessario avvolgere context.exception in str , altrimenti si otterrà un TypeError

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

Come si può scrivere un unittest che fallisce solo se una funzione non genera un'eccezione prevista?




assertRaises un'occhiata al metodo unittest modulo unittest .




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

Innanzitutto, ecco la funzione corrispondente (ancora dum: p) nel file 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

Ecco il test da eseguire (solo questo test è inserito):

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()

Ora siamo pronti a testare la nostra funzione! Ecco cosa succede quando provi a eseguire il 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)

L'errore TypeEride viene sollevato e genera un errore di test. Il problema è che questo è esattamente il comportamento che volevamo: s.

Per evitare questo errore, esegui semplicemente la funzione usando lambda nella chiamata di prova:

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

L'output finale:

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

OK

Perfetto!

... e anche per me è perfetto !!

Grazie mille signor Julien Lengrand-Lambert




È possibile creare il proprio contextmanager per verificare se è stata sollevata l'eccezione.

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

E poi puoi usare raises come questo:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

Se stai usando pytest , questa cosa è già implementata. Puoi fare pytest.raises(Exception) :

Esempio:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

E il risultato:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED



Come si verifica se una funzione Python genera un'eccezione?

Come si scrive un test che fallisce solo se una funzione non genera un'eccezione prevista?

Risposta breve:

Utilizza il metodo self.assertRaises come gestore del contesto:

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

Dimostrazione

L'approccio delle migliori pratiche è abbastanza semplice da dimostrare in una shell Python.

La biblioteca unittest

In Python 2.7 o 3:

import unittest

In Python 2.6, puoi installare un backport della libreria unittest di 2.7, chiamato unittest2 , e alias semplicemente come unittest :

import unittest2 as unittest

Test di esempio

Ora, incolla nella tua shell Python il seguente test della sicurezza del tipo di 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')

Il test 1 utilizza assertRaises come gestore del contesto, che assicura che l'errore venga catturato e pulito correttamente, mentre è registrato.

Potremmo anche scriverlo senza il gestore di contesto, vedi test due. Il primo argomento sarebbe il tipo di errore che ci si aspetta di aumentare, il secondo argomento, la funzione che si sta testando e gli argomenti rimanenti e gli argomenti di parole chiave verranno passati a quella funzione.

Penso che sia molto più semplice, leggibile e mantenibile solo per usare il gestore di contesto.

Esecuzione dei test

Per eseguire i test:

unittest.main(exit=False)

In Python 2.6, probabilmente avrai bisogno di quanto segue :

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

E il tuo terminale dovrebbe produrre quanto segue:

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

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

E vediamo che, come ci aspettiamo, il tentativo di aggiungere un 1 e un risultato '1' in un TypeError .

Per un output più dettagliato, prova questo:

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