Jak sprawdzić, czy funkcja Pythona rzuca wyjątek?
Jak napisać unittest, który nie powiedzie się tylko wtedy, gdy funkcja nie wyrzuci oczekiwanego wyjątku?
11 answers
Użycie TestCase.assertRaises
(lub TestCase.failUnlessRaises
) z modułu unittest, na przykład:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-05 15:59:38
Od wersji Python 2.7 można użyć menedżera kontekstu, aby uzyskać dostęp do rzeczywistego obiektu wyjątku rzuconego:
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
W Python 3.5 , musisz zawinąć context.exception
w str
, w przeciwnym razie otrzymasz TypeError
self.assertTrue('This is broken' in str(context.exception))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 06:26:18
Kod w mojej poprzedniej odpowiedzi można uprościć do:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
A jeśli afunkcja pobiera argumenty, po prostu przekaż je do twierdzeń w następujący sposób:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 13:37:56
Jak sprawdzić, czy funkcja Pythona rzuca wyjątek?
Jak napisać test, który nie powiedzie się tylko wtedy, gdy funkcja nie rzuci oczekiwany wyjątek?
Krótka Odpowiedź:
Użyj metody self.assertRaises
jako menedżera kontekstu:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Demonstracja
Podejście oparte na najlepszych praktykach jest dość łatwe do zademonstrowania w powłoce Pythona.
Biblioteka unittest
W Pythonie 2.7 lub 3:
import unittest
W Pythonie 2.6 możesz zainstalować backport biblioteki unittest
2.7 o nazwie unittest2 , i po prostu alias unittest
:
import unittest2 as unittest
Przykładowe testy
Teraz wklej do powłoki Pythona następujący test bezpieczeństwa typu Pythona:
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 używa assertRaises
jako menedżera kontekstu, który zapewnia, że błąd zostanie prawidłowo wychwycony i wyczyszczony podczas nagrywania.
Możemy go również napisać BEZ menedżera kontekstu, zobacz test drugi. Pierwszym argumentem będzie typ błędu, który chcesz wywołać, drugim argument, funkcja, którą testujesz, a pozostałe args i słowo kluczowe args zostaną przekazane do tej funkcji.
Myślę, że o wiele prostsze, czytelniejsze i łatwiejsze do utrzymania jest użycie Menedżera kontekstu.
Uruchamianie testów
Aby uruchomić testy:
unittest.main(exit=False)
W Pythonie 2.6 prawdopodobnie będziesz potrzebował następującej :
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
I twój terminal powinien wyjście:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
I widzimy to tak, jak się spodziewamy, próbując dodać 1
i '1'
w wyniku TypeError
.
Aby uzyskać więcej informacji, spróbuj tego:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:43
Twój kod powinien podążać za tym wzorcem (jest to test stylu modułu unittest):
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')
W Pythonie assertRaises sprawdza tylko, czy został zgłoszony wyjątek.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-05 16:08:52
From: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
Po pierwsze, tutaj jest odpowiednia (jeszcze dum :p) funkcja w pliku 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
Oto test do wykonania (tylko ten test jest wstawiony):
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()
Jesteśmy teraz gotowi do przetestowania naszej funkcji! Oto, co się dzieje podczas próby uruchomienia testu:
======================================================================
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)
TypeError jest wywoływany przez actullay i generuje błąd testu. Problem w tym, że jest to dokładnie zachowanie, którego chcieliśmy: s.
Aby uniknąć tego błędu, po prostu uruchom funkcję lambda w wywołaniu testowym:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
Wynik końcowy:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Doskonale !
... i dla mnie też jest idealny!!
Thansk a lot Mr. Julien Lengrand-Lambert
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-05 17:04:37
Używam doctest [1] prawie wszędzie, ponieważ podoba mi się fakt, że jednocześnie dokumentuję i testuję moje funkcje.
Spójrz na ten kod:
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()
Jeśli umieścisz ten przykład w module i uruchomisz go z linii poleceń, oba przypadki testowe są oceniane i sprawdzane.
[1] Dokumentacja Pythona: 23.2 doctest -- test interaktywnych przykładów Pythona
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-25 11:17:18
Spójrz na metodę assertRaises modułu unittest
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-21 12:57:25
Właśnie odkryłem, że Mock library dostarcza metody assertRaisesWithMessage () (w swojej jednostce.TestCase subclass), która sprawdzi nie tylko, czy wywołany jest oczekiwany wyjątek, ale także czy został wywołany z oczekiwaną wiadomością:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-28 00:13:39
Możesz zbudować swój własny contextmanager
, aby sprawdzić, czy wyjątek został podniesiony.
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
I wtedy możesz użyć raises
w ten sposób:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Jeśli używasz pytest
, ta rzecz jest już zaimplementowana. Możesz zrobić pytest.raises(Exception)
:
Przykład:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
I wynik:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 10:09:17
Możesz użyć assertRaises z modułu unittest
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-21 00:12:46