Как мы должны проверять исключения с носом?
Я тестирую исключения с носом. Вот пример:
def testDeleteUserUserNotFound(self):
"Test exception is raised when trying to delete non-existent users"
try:
self.client.deleteUser('10000001-0000-0000-1000-100000000000')
# make nose fail here
except UserNotFoundException:
assert True
Утверждение выполняется, если возникает исключение, но если исключение не создано, оно не будет выполнено.
Есть ли что-нибудь, что я могу добавить в прокомментированную строку выше, чтобы, если исключение не возбуждено, сообщение сообщит об ошибке?
Ответы
Ответ 1
нос предоставляет инструменты для тестирования исключений (например, unittest).
Попробуйте этот пример (и прочитайте о других инструментах в Инструментах тестирования носа
from nose.tools import *
l = []
d = dict()
@raises(Exception)
def test_Exception1():
'''this test should pass'''
l.pop()
@raises(KeyError)
def test_Exception2():
'''this test should pass'''
d[1]
@raises(KeyError)
def test_Exception3():
'''this test should fail (IndexError raised but KeyError was expected)'''
l.pop()
def test_Exception4():
'''this test should fail with KeyError'''
d[1]
Я бы подумал, что это правильный способ, который вы искали, потому что он позволяет вам быть конкретным относительно тех исключений, которые вы ожидаете или хотите. Поэтому вы действительно вызываете ошибку, чтобы увидеть, что она вызывает правильное исключение. И затем вы даете носу оценивать результат. (Поместите как можно меньше логики в модульные тесты!)
Ответ 2
def testDeleteUserUserNotFound(self):
"Test exception is raised when trying to delete non-existent users"
try:
self.client.deleteUser('10000001-0000-0000-1000-100000000000')
assert False # <---
except UserNotFoundException:
assert True
Из семантики try
/except
следует, что поток выполнения оставляет блок try
в исключении, поэтому assert False
не будет выполняться, если возникает исключение. Кроме того, выполнение не будет повторно вводить блок try
снова после выполнения блока except
, поэтому вы не должны столкнуться с трудностями.
↓
(statements)
↓ exception
(try) ↚──────────→ (except)
↓ │
(statements) ←───────────┘
↓
Ответ 3
Я не знаю, почему этого еще нет, но существует еще один способ:
import unittest
class TestCase(unittest.TestCase):
def testKeyError(self):
d = dict()
with self.assertRaises(KeyError):
d[1]
Ответ 4
Я настоятельно рекомендую использовать 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
не был тем, который мы хотели.
Ответ 5
Я не знаю, что такое нос, но вы пытались использовать "else" после предложения except. То есть
else:
assert False
Ответ 6
Используйте assert_raises
:
from nose.tools import assert_raises
our_method = self.client.deleteUser
arg1 = '10000001-0000-0000-1000-100000000000'
expected_exception = UserNotFoundException
assert_raises(expected_exception, our_method, arg1)
Использование try и catch в ваших тестах кажется плохой практикой (в большинстве случаев).
Нет никакой конкретной документации в нос, потому что она в основном просто обертка unittest.TestCase.assertRaises (ссылка Как использовать assert_raises носа?)