Как написать unittest, который терпит неудачу, только если функция не генерирует ожидаемое исключение?
Как вы проверяете, что функция Python генерирует исключение?
Ответ 1
Используйте TestCase.assertRaises
(или TestCase.failUnlessRaises
) из модуля unittest, например:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
Ответ 2
Начиная с Python 2.7, вы можете использовать менеджер контекста, чтобы получить реальный объект Exception:
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
В Python 3.5 вы должны обернуть context.exception
в str
, иначе вы получите TypeError
self.assertTrue('This is broken' in str(context.exception))
Ответ 3
Код в моем предыдущем ответе можно упростить, чтобы:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
И если afunction принимает аргументы, просто передайте их в assertRaises следующим образом:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
Ответ 4
Как вы проверяете, что функция Python генерирует исключение?
Как написать тест, который терпит неудачу, только если функция не бросает ожидаемое исключение?
Краткий ответ:
Используйте метод self.assertRaises
в качестве менеджера контекста:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Демонстрация
Практический подход довольно легко продемонстрировать в оболочке Python.
Библиотека unittest
В Python 2.7 или 3:
import unittest
В Python 2.6 вы можете установить backport из библиотеки 2.7 unittest
, называемой unittest2, и просто псевдоним, который как unittest
:
import unittest2 as unittest
Примеры тестов
Теперь вставьте в свою оболочку Python следующий тест безопасности типа 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')
В тестовом режиме используется assertRaises
как менеджер контекста, который гарантирует, что ошибка будет правильно захвачена и очищена при записи.
Мы могли бы написать его без менеджера контекста, см. тест два. Первым аргументом будет тип ошибки, которую вы ожидаете поднять, второй аргумент, тестируемая функция, а остальные аргументы args и ключевого слова будут переданы этой функции.
Я думаю, что это намного проще, легко читаемо и поддерживается только для использования диспетчера контекстов.
Выполнение тестов
Запуск тестов:
unittest.main(exit=False)
В Python 2.6 вам, возможно, потребуется следующее:
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
И ваш терминал должен вывести следующее:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
И мы видим, что, как мы ожидаем, попытка добавить 1
и a '1'
приведет к TypeError
.
Для получения более подробного вывода попробуйте следующее:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Ответ 5
Ваш код должен следовать этому шаблону (это тест стиля модуля unittest):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
На Python & lt; 2.7 эта конструкция полезна для проверки конкретных значений в ожидаемом исключении. Функция unittest assertRaises
только проверяет, возникло ли исключение.
Ответ 6
from: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
Во-первых, вот соответствующая функция (еще dum: p) в файле 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
Вот тест, который нужно выполнить (только этот тест вставлен):
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()
Теперь мы готовы проверить нашу функцию! Вот что происходит при попытке запустить тест:
======================================================================
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 активируется и генерирует отказ теста. Проблема в том, что это именно то, что мы хотели: s.
Чтобы избежать этой ошибки, просто запустите функцию, используя лямбда в тестовом вызове:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
Конечный результат:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Идеально!
... и для меня тоже идеально!
Танск много г-н Жюльен Ленгранд-Ламберт
Ответ 7
Вы можете создать свой собственный contextmanager
, чтобы проверить, было ли возбуждено исключение.
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
И тогда вы можете использовать raises
следующим образом:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Если вы используете pytest
, эта вещь уже реализована. Вы можете сделать pytest.raises(Exception)
:
Пример:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
И результат:
[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
Ответ 8
Посмотрите на assertRaises способе unittest
модуля.
Ответ 9
Я использую doctest [1] почти везде, потому что мне нравится тот факт, что я документирую и тестирую свои функции одновременно.
Посмотрите на этот код:
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()
Если вы поместите этот пример в модуль и запустите его из командной строки, оба тестовых примера оцениваются и проверяются.
[1] Документация Python: 23.2 doctest - Тестирование интерактивных примеров Python
Ответ 10
Я только что обнаружил, что библиотека Mock предоставляет метод assertRaisesWithMessage() (в своем подклассе unittest.TestCase), который будет проверять не только то, что вызывается ожидаемое исключение, но и то, что оно вызывается с ожидаемым сообщением:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Ответ 11
Вы можете использовать assertRaises из модуля 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)
Ответ 12
Здесь много ответов. Код показывает, как мы можем создать Исключение, как мы можем использовать это исключение в наших методах и, наконец, как вы можете проверить в модульном тесте правильные возбуждаемые исключения.
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()
Ответ 13
Несмотря на то, что все ответы в порядке, я искал способ проверить, вызвала ли функция исключение, не полагаясь на рамки модульного тестирования и не создавая тестовые классы.
В итоге я написал следующее:
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)
И это не на правой строке:
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