Я настоятельно рекомендую использовать assert_raises
и assert_raises_regexp
из nose.tools
, которые дублируют поведение assertRaises
и assertRaisesRegexp
из unittest.TestCase
. Это позволяет использовать те же функциональные возможности, которые предоставляет unittest.TestCase
в тестовых наборах, которые фактически не используют класс unittest.TestCase
.
Я считаю, что @raises
слишком тупой инструмент. Вот код, иллюстрирующий проблему:
from nose.tools import *
something = ["aaa", "bbb"]
def foo(x, source=None):
if source is None:
source = something
return source[x]
# This is fine
@raises(IndexError)
def test1():
foo(3)
# This is fine. The expected error does not happen because we made
# a mistake in the test or in the code. The failure indicates we made
# a mistake.
@raises(IndexError)
def test2():
foo(1)
# This passes for the wrong reasons.
@raises(IndexError)
def test3():
source = something[2] # This is the line that raises the exception.
foo(10, source) # This is not tested.
# When we use assert_raises, we can isolate the line where we expect
# the failure. This causes an error due to the exception raised in
# the first line of the function.
def test4():
source = something[2]
with assert_raises(IndexError):
foo(10, source)
test3
проходит, но не потому, что foo
вызвал исключение, которое мы ожидали, а потому, что код, который устанавливает данные, которые будут использоваться foo
, завершается с ошибкой с тем же исключением. test4
показывает, как можно написать тест, используя assert_raises
, чтобы фактически проверить, что мы подразумеваем под тестированием. Проблема в первой строке приведет к тому, что Nose сообщит об ошибке, а затем мы сможем переписать тест, чтобы эта строка позволила нам, наконец, протестировать то, что мы собирались проверить.
@raises
не позволяет проверить сообщение, связанное с исключением. Когда я поднимаю ValueError
, просто для примера, я обычно хочу поднять его с информативным сообщением. Вот пример:
def bar(arg):
if arg: # This is incorrect code.
raise ValueError("arg should be higher than 3")
if arg >= 10:
raise ValueError("arg should be less than 10")
# We don't know which of the possible `raise` statements was reached.
@raises(ValueError)
def test5():
bar(10)
# Yes, we're getting an exception but with the wrong value: bug found!
def test6():
with assert_raises_regexp(ValueError, "arg should be less than 10"):
bar(10)
test5
, который использует @raises
, пройдет, но пройдет по неправильной причине. test6
выполняет более тонкий тест, который показывает, что поднятый ValueError
не тот, который мы хотели.