2008-09-24 10 views

Antwort

379

Verwendung TestCase.assertRaises (oder TestCase.failUnlessRaises) aus dem Unittest-Modul, zum Beispiel:

import mymod 

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

ist es eine Möglichkeit, das Gegenteil von dem zu tun? Wie es nur fehlschlägt, wenn die Funktion die Ausnahme auslöst? – BUInvent

+4

@BUInvent, ja, es gibt: https://StackOverflow.com/a/4319870/3405140 – moertel

+0

Beachten Sie, dass Argumente zum Argument "myfunc" übergeben werden müssen, um sie dem assertRaises-Aufruf als Argumente hinzuzufügen. Siehe Daryl Spitzers Antwort. – cbron

6

Schauen Sie sich die assertRaises Methode des unittest Moduls an.

24

Code soll dieses Muster folgen (dies ist ein Unittest-Modul-Test):

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

Auf Python < 2.7 Dieses Konstrukt ist nützlich, um nach bestimmten Werten in der erwarteten Ausnahme zu suchen. Die Unitest-Funktion assertRaises prüft nur, ob eine Ausnahme ausgelöst wurde.

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

Und wenn eine Funktion Argumente übernimmt, so dass sie nur in assertRaises wie dies passieren:

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

und Methode self.fail benötigt nur ein Argument – mdob

+3

Dies scheint zu kompliziert zu sein, wenn eine Funktion eine Ausnahme auslöst. Da jede Ausnahme außer dieser Ausnahme einen Fehler beim Test verursacht und keine Ausnahme auslöst, wird der Test fehlschlagen. Der einzige Unterschied ist, dass wenn Sie eine andere Ausnahme mit 'assertRaises 'erhalten, ein FEHLER anstelle eines FEHLERS angezeigt wird. – unflores

160

Der Code in meiner vorherigen Antwort kann vereinfacht werden ] fast überall, weil es mir gefällt, dass ich meine Funktionen gleichzeitig dokumentiere und teste.

Werfen Sie einen Blick auf diesem 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() 

Wenn Sie dieses Beispiel in einem Modul setzen und es von der Kommandozeile beiden Testfälle werden ausgewertet und überprüft laufen.

[1] Python documentation: 23.2 doctest -- Test interactive Python examples

+0

Wie gehen Sie mit der Rückverfolgung der Ausnahme um, die in der Konsole vorhanden ist? Das heißt, es gibt jetzt eine Rückverfolgung in dem ansonsten bestandenen Test. – MadPhysicist

+0

@MadPhysicist Ich verstehe nicht, wovon du sprichst. Ich habe gerade auf Python 2.6 und 3.5 getestet - keine Tracebacks werden auf der Konsole gedruckt. Vielleicht drucken oder protokollieren Sie die Rückverfolgung irgendwo im Code? –

7

Ich benutze doctest [1

+4

Ich liebe doctest, aber ich finde es ergänzt eher als Unittest ersetzt. – TimothyAWiseman

+2

Ist doctest weniger wahrscheinlich, mit automatisiertem Refactoring nett zu spielen? Ich nehme an, ein Refactoring-Tool, das für Python * entwickelt wurde, sollte sich der Docstrings bewusst sein. Kann jemand aus ihrer Erfahrung kommentieren? – kdbanman

4

Ich habe gerade entdeckt, dass die Mock library eine assertRaisesWithMessage() -Methode (in seiner unittest.TestCase Unterklasse) bietet, die nur überprüft nicht, dass die erwartete Ausnahme angehoben wird, sondern auch, dass es mit der erwarteten Nachricht angehoben:

from testcase import TestCase 

import mymod 

class MyTestCase(TestCase): 
    def test1(self): 
     self.assertRaisesWithMessage(SomeCoolException, 
            'expected message', 
            mymod.myfunc) 
+0

Leider bietet es es nicht mehr .. Aber die obige Antwort von @Art (http://StackOverflow.com/A/3166985/1504046) gibt das gleiche Ergebnis – Rmatt

255

Seit Python 2.7 Sie Kontext-Manager einen Halt des eigentlichen Exception-Objekts geworfen bekommen können:

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, haben Sie context.exception in str wickeln, sonst werden Sie ein TypeError

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

Ich benutze Python 2.7.10, und die oben funktioniert nicht; 'context.exception' gibt die Nachricht nicht an; es ist ein Typ. – LateCoder

+3

Auch in Python 2.7 (mindestens in meinem 2.7.6) mit 'import unittest2' müssen Sie' str() 'verwenden, dh' self.assertTrue ('Das ist kaputt' in str (context.exception)) ' . –

+1

Zwei Dinge: 1. Sie können assertIn anstelle von assertTrue verwenden. Z.B. self.assertIn ('This is broken', context.exception) 2. Bei Verwendung von 2.7.10 scheint context.exception ein Array von Zeichen zu sein. Die Verwendung von str funktioniert nicht. Ich landete dies zu tun: '' .join (context.exception) also zusammen: self.assertIn ('Dies ist gebrochen', '' .join (context.exception)) – blockcipher

0

bekommen Sie assertRaises aus dem Unittest-Modul verwenden

import unittest 

class TestClass(): 
    def raises_exception(self): 
    raise Exception("test") 

class MyTestCase(unittest.TestCase): 
    def test_if_method_raises_correct_exception(self): 
    test_class = TestClass() 
    # note that you dont use() when passing the method to assertRaises 
    self.assertRaises(Exception, test_class.raises_exception) 
7

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

Zuerst, hier ist die entsprechende (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 Test durchgeführt werden soll (nur dieser Test eingesetzt wird):

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 ist, was passiert, wenn man versucht, 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) 

Das ist actullay Typeerror erhöht und erzeugt ein Testversagen. Das Problem ist, dass dies genau das Verhalten ist, das wir wollten: s.

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

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

Die endgültige Ausgabe:

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

OK 

Perfect!

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

Thansk viel Herr Julien Lengrand-Lambert

+8

Nur eine Anmerkung, 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 lassen Sie den Unittest aufrufen: 'self.assertRaises (TypeError, df.square_value, self.false_int)' –

2

Sie bauen können Ihre eigenen contextmanager zu überprüfen, ob die Exception 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 folgt verwenden:

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

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

Wenn Sie pytest verwenden, wird diese Sache bereits umgesetzt. Sie können pytest.raises(Exception) tun:

Beispiel:

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

Und das Ergebnis:

[email protected]$ 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 
73

Wie testen Sie, dass eine Python-Funktion eine Ausnahme auslöst?

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

Kurze Antwort:

Verwenden Sie die self.assertRaises Methode als Kontext-Manager:

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

Demonstration

Der Best-Practice-Ansatz ist ziemlich einfach, in einem Python-Shell zu demonstrieren.

Die unittest Bibliothek

In Python 2.7 oder 3:

import unittest 

In Python 2.6, Sie unittest Bibliothek eine Rückportierung von 2,7 des installieren können, genannt unittest2 und nur alias dass als unittest:

import unittest2 as unittest 

Beispiel testet

Fügen Sie jetzt in Ihre Python-Shell den folgenden Test von Pythons Typ-Sicherheit ein:

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 verwendet assertRaises als Kontextmanager, der sicherstellt, dass der Fehler ordnungsgemäß erfasst und bereinigt wird, während er aufgezeichnet wird.

Wir könnten es auch schreiben ohne der Kontextmanager, siehe Test zwei. Das erste Argument wäre der Fehlertyp, den Sie erwarten, das zweite Argument, die Funktion, die Sie testen, und die verbleibenden Argumente und Schlüsselwortargumente werden an diese Funktion übergeben.

Ich denke, es ist viel einfacher, lesbar und wartbar, nur um den Kontextmanager zu verwenden.

Ausführen der Tests

die Tests auszuführen:

unittest.main(exit=False) 

In Python 2.6, werden Sie wahrscheinlich need the following:

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

Und Ihr Terminal sollte eine Ausgabe wie folgt vor:

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

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

Und wir sehen, dass, wie wir erwarten, versuchen, eine 1 und eine '1' Ergebnis in eine TypeError hinzuzufügen.


Weitere ausführliche Ausgabe, versuchen Sie dies:

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