Подтвердить что ты не робот

Как мы должны проверять исключения с носом?

Я тестирую исключения с носом. Вот пример:

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

Утверждение выполняется, если возникает исключение, но если исключение не создано, оно не будет выполнено.

Есть ли что-нибудь, что я могу добавить в прокомментированную строку выше, чтобы, если исключение не возбуждено, сообщение сообщит об ошибке?

4b9b3361

Ответ 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 носа?)