1230 Stimmen

Wie testet man, dass eine Python-Funktion eine Ausnahme auslöst?

Wie kann man einen Unittest schreiben, der nur dann fehlschlägt, wenn eine Funktion keine erwartete Ausnahme auslöst?

50voto

macm Punkte 1859

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

Hier ist zunächst die entsprechende (immer noch dum :p) Funktion in der Datei 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

Hier ist der durchzuführende Test (nur dieser Test wird eingefügt):

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

Wir sind nun bereit, unsere Funktion zu testen! Hier sehen Sie, was passiert, wenn Sie versuchen, den Test auszuführen:

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

Der TypeError wird sofort ausgelöst und erzeugt einen Testfehler. Das Problem ist, dass dies genau das Verhalten ist, das wir wollten :s.

Um diesen Fehler zu vermeiden, führen Sie die Funktion einfach mit Lambda im Testaufruf aus:

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

Die endgültige Ausgabe :

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

OK

Perfekt!

... und für mich ist auch perfekt!!

Vielen Dank, Herr Julien Lengrand-Lambert


Diese Test-Assert gibt tatsächlich eine falsch positiv . Dies geschieht, weil das Lambda innerhalb von "assertRaises" die Einheit ist, die einen Typfehler auslöst und no die getestete Funktion.

17 Stimmen

Nur ein Hinweis: Sie brauchen das Lambda nicht. Die Zeile self.assertRaises(TypeError, df.square_value(self.false_int)) ruft die Methode auf und gibt das Ergebnis zurück. Was Sie wollen, ist, die Methode und alle Argumente zu übergeben und den Unittest sie aufrufen zu lassen: self.assertRaises(TypeError, df.square_value, self.false_int)

1 Stimmen

Und im Jahr 2021 entdeckte ein Freund von mir, dass mein Blog zur Beantwortung der Frage verwendet wurde. Danke für die Anerkennung, @macm!

42voto

kriss Punkte 22473

Da ich keine detaillierte Erklärung gesehen habe, wie zu überprüfen, ob wir eine bestimmte Ausnahme unter einer Liste von akzeptierten mit Kontext-Manager oder andere Ausnahme Details bekam, werde ich meine hinzufügen (überprüft auf Python 3.8).

Wenn ich nur überprüfen möchte, ob die Funktion beispielsweise TypeError würde ich schreiben:

with self.assertRaises(TypeError):
    function_raising_some_exception(parameters)

Wenn ich überprüfen möchte, ob die Funktion entweder TypeError o IndexError würde ich schreiben:

with self.assertRaises((TypeError,IndexError)):
    function_raising_some_exception(parameters)

Und wenn ich noch mehr Einzelheiten über die angesprochene Ausnahme erfahren möchte, könnte ich sie in einem Kontext wie diesem finden:

# Here I catch any exception    
with self.assertRaises(Exception) as e:
    function_raising_some_exception(parameters)

# Here I check actual exception type (but I could
# check anything else about that specific exception,
# like it's actual message or values stored in the exception)
self.assertTrue(type(e.exception) in [TypeError,MatrixIsSingular])

4 Stimmen

Diese Antwort ist außerordentlich (ha!) nützlich, weil sie eine Typüberprüfung und einen Vorschlag zur Überprüfung von etwas anderem enthält. Dies half mir, die explizite Fehlermeldung mit self.assertEqual(e.exception.args[0], "Values cannot be a generator. Use list(generator) instead.",) .

0 Stimmen

Kontextgesteuerte Fehler sind die besten. Sie ermöglichen es Ihnen, auch die Fehlermeldung zu testen!

38voto

Pigueiras Punkte 17990

Wenn Sie Folgendes verwenden pytest können Sie pytest.raises(Exception) :

Beispiel:

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

Und das Ergebnis:

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

Oder Sie können Ihr eigenes Modell bauen contextmanager um zu prüfen, ob die Ausnahme ausgelöst wurde.

import contextlib

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

Und dann können Sie raises wie diese:

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

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

5 Stimmen

Danke für eine Antwort, die nicht die unittest Modul!

20voto

ifedapo olarewaju Punkte 2391

Wenn Sie Python 3 verwenden, können Sie, um eine Ausnahme zusammen mit der zugehörigen Meldung zu bestätigen, Folgendes verwenden assertRaises im Kontextmanager und übergeben die Nachricht als msg Schlüsselwortargument wie folgt:

import unittest

def your_function():
    raise RuntimeError('your exception message')

class YourTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(RuntimeError, msg='your exception message'):
            your_function()

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

3 Stimmen

msg in meinem Fall nichts tun.

0 Stimmen

@egvo mit welcher Version von Python haben Sie dies versucht?

0 Stimmen

Jetzt habe ich es mit 3.10.5 und vorher mit einigen 3.9 auf meinem Laptop zu Hause überprüft. Ich änderte msg und es hat nicht reagiert. assertRaisesRegex macht es richtig.

11voto

pi. Punkte 20053

Ich benutze Doktortest [1] fast überall, weil ich es mag, dass ich meine Funktionen gleichzeitig dokumentieren und testen kann.

Schauen Sie sich diesen Code an:

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

Wenn Sie dieses Beispiel in ein Modul einfügen und es von der Kommandozeile aus ausführen, werden beide Testfälle ausgewertet und geprüft.

[1] Python-Dokumentation: 23.2 doctest -- Interaktive Python-Beispiele testen

4 Stimmen

Ich liebe Doctest, aber ich finde, es ergänzt Unittest eher, als dass es es ersetzt.

3 Stimmen

Ist es unwahrscheinlich, dass Doctest mit automatisiertem Refactoring zusammenarbeitet? Ich nehme an, ein Refactoring-Tool, das für Python entwickelt wurde sollte achten Sie auf docstrings. Kann jemand aus seiner Erfahrung berichten?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X