Wie schreibt man eine Unittest, die nur fehlschlägt, wenn eine Funktion keine erwartete Ausnahme auslöst?
Wie schreibt man eine Unittest, die nur fehlschlägt, wenn eine Funktion keine erwartete Ausnahme auslöst?
Antworten:
Verwenden Sie 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)
myfunc
Sie zum Übergeben von Argumenten diese als Argumente zum assertRaises-Aufruf hinzufügen müssen. Siehe Daryl Spitzers Antwort.
self.assertRaises(TypeError, mymod.myfunc)
. Eine vollständige Liste der integrierten Ausnahmen finden Sie hier: docs.python.org/3/library/exceptions.html#bltin-exceptions
self.assertRaises(SomeCoolException, Constructor, arg1)
Seit Python 2.7 können Sie den Kontextmanager verwenden, um das tatsächlich ausgelöste Exception-Objekt abzurufen:
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 , müssen Sie wickeln context.exception
in str
, sonst werden Sie eine bekommenTypeError
self.assertTrue('This is broken' in str(context.exception))
context.exception
gibt die Nachricht nicht; Es ist ein Typ.
import unittest2
müssen Sie verwenden str()
, dh self.assertTrue('This is broken' in str(context.exception))
.
Der Code in meiner vorherigen Antwort kann vereinfacht werden zu:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
Und wenn eine Funktion Argumente benötigt, geben Sie sie einfach an assertRaises wie folgt weiter:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
2.7.15
. Wenn afunction
in self.assertRaises(ExpectedException, afunction, arg1, arg2)
der Klasseninitialisierer ist, müssen Sie self
als erstes Argument übergeben, z. B. self.assertRaises(ExpectedException, Class, self, arg1, arg2)
Wie testest du, dass eine Python-Funktion eine Ausnahme auslöst?
Wie schreibt man einen Test, der nur fehlschlägt, wenn eine Funktion keine erwartete Ausnahme auslöst?
Verwenden Sie die self.assertRaises
Methode als Kontextmanager:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Der Best-Practice-Ansatz lässt sich in einer Python-Shell recht einfach demonstrieren.
Die unittest
Bibliothek
In Python 2.7 oder 3:
import unittest
In Python 2.6 können Sie einen Backport der unittest
Bibliothek von 2.7 mit dem Namen unittest2 installieren und den folgenden Alias verwenden unittest
:
import unittest2 as unittest
Fügen Sie nun den folgenden Test der Typensicherheit von Python in Ihre Python-Shell 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 One wird assertRaises
als Kontextmanager verwendet, der sicherstellt, dass der Fehler während der Aufzeichnung ordnungsgemäß abgefangen und bereinigt wird.
Wir könnten es auch ohne den Kontextmanager schreiben , siehe Test zwei. Das erste Argument ist der Fehlertyp, den Sie voraussichtlich auslösen werden, das zweite Argument, die zu testende Funktion und die verbleibenden Argumente und Schlüsselwortargumente werden an diese Funktion übergeben.
Ich denke, es ist viel einfacher, lesbarer und wartbarer, nur den Kontextmanager zu verwenden.
So führen Sie die Tests aus:
unittest.main(exit=False)
In Python 2.6 benötigen Sie wahrscheinlich Folgendes :
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Und Ihr Terminal sollte Folgendes ausgeben:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
Und wir sehen das wie erwartet, wenn wir versuchen, a 1
und ein '1'
Ergebnis in a hinzuzufügen TypeError
.
Versuchen Sie Folgendes, um eine ausführlichere Ausgabe zu erhalten:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Ihr Code sollte diesem Muster folgen (dies ist ein ungeeigneter Test im Modulstil):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
Unter Python <2.7 ist dieses Konstrukt nützlich, um in der erwarteten Ausnahme nach bestimmten Werten zu suchen. Die Funktion unittest assertRaises
prüft nur, ob eine Ausnahme ausgelöst wurde.
assertRaises
erhalten, einen FEHLER anstelle eines FEHLERS erhalten.
von: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
Hier ist zunächst 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 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 jetzt bereit, unsere Funktion zu testen! Folgendes passiert, wenn versucht wird, 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 aktiv ausgelöst und generiert einen Testfehler. Das Problem ist, dass dies genau das Verhalten ist, das wir wollten: s.
Um diesen Fehler zu vermeiden, führen Sie einfach die Funktion 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 es auch perfekt !!
Vielen Dank Herr Julien Lengrand-Lambert
Diese Testaussage gibt tatsächlich ein falsches Positiv zurück . Dies geschieht, weil das Lambda in 'assertRaises' die Einheit ist, die einen Typfehler auslöst, und nicht die getestete Funktion.
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 die Unittest es nennen zu lassen:self.assertRaises(TypeError, df.square_value, self.false_int)
Sie können Ihre eigenen erstellen contextmanager
, um zu überprü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 so verwenden raises
:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Wenn Sie verwenden pytest
, ist dieses Ding bereits implementiert. Sie können tun 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
unittest
Modul nicht erforderlich ist !
Ich benutze doctest [1] fast überall, weil mir die Tatsache gefällt, dass ich meine Funktionen gleichzeitig dokumentiere und teste .
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 über die Befehlszeile ausführen, werden beide Testfälle ausgewertet und überprüft.
[1] Python-Dokumentation: 23.2 doctest - Testen Sie interaktive Python-Beispiele
Ich habe gerade festgestellt, dass die Mock-Bibliothek eine assertRaisesWithMessage () -Methode (in ihrer unittest.TestCase-Unterklasse) bereitstellt, die nicht nur überprüft, ob die erwartete Ausnahme ausgelöst wird, sondern auch, ob sie mit der erwarteten Nachricht ausgelöst wird:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Hier gibt es viele Antworten. Der Code zeigt, wie wir eine Ausnahme erstellen können, wie wir diese Ausnahme in unseren Methoden verwenden können und wie Sie schließlich in einem Komponententest überprüfen können, ob die richtigen Ausnahmen ausgelöst werden.
import unittest
class DeviceException(Exception):
def __init__(self, msg, code):
self.msg = msg
self.code = code
def __str__(self):
return repr("Error {}: {}".format(self.code, self.msg))
class MyDevice(object):
def __init__(self):
self.name = 'DefaultName'
def setParameter(self, param, value):
if isinstance(value, str):
setattr(self, param , value)
else:
raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)
def getParameter(self, param):
return getattr(self, param)
class TestMyDevice(unittest.TestCase):
def setUp(self):
self.dev1 = MyDevice()
def tearDown(self):
del self.dev1
def test_name(self):
""" Test for valid input for name parameter """
self.dev1.setParameter('name', 'MyDevice')
name = self.dev1.getParameter('name')
self.assertEqual(name, 'MyDevice')
def test_invalid_name(self):
""" Test to check if error is raised if invalid type of input is provided """
self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)
def test_exception_message(self):
""" Test to check if correct exception message and code is raised when incorrect value is passed """
with self.assertRaises(DeviceException) as cm:
self.dev1.setParameter('name', 1234)
self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')
if __name__ == '__main__':
unittest.main()
Sie können 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)
Obwohl alle Antworten vollkommen in Ordnung sind, suchte ich nach einer Möglichkeit zu testen, ob eine Funktion eine Ausnahme auslöst, ohne sich auf Unit-Test-Frameworks zu verlassen und Testklassen schreiben zu müssen.
Am Ende schrieb ich Folgendes:
def assert_error(e, x):
try:
e(x)
except:
return
raise AssertionError()
def failing_function(x):
raise ValueError()
def dummy_function(x):
return x
if __name__=="__main__":
assert_error(failing_function, 0)
assert_error(dummy_function, 0)
Und es scheitert auf der richtigen Linie:
Traceback (most recent call last):
File "assert_error.py", line 16, in <module>
assert_error(dummy_function, 0)
File "assert_error.py", line 6, in assert_error
raise AssertionError()
AssertionError