我们应该如何用鼻子测试异常?

发布于 2024-12-10 13:37:14 字数 410 浏览 0 评论 0 原文

我正在用鼻子测试异常。这是一个例子:

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

如果引发异常,则执行断言,但如果没有引发异常,则不会执行。

我可以在上面的注释行添加任何内容,这样如果没有引发异常,鼻子就会报告失败吗?

I'm testing exceptions with nose. Here's an example:

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

The assert is executed if the exception is raised, but if no exception is raised, it won't be executed.

Is there anything I can put on the commented line above so that if no exception is raised nose will report a failure?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

可爱咩 2024-12-17 13:37:14

nose 提供了测试异常的工具(就像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]

尝试这个例子(并阅读鼻子测试工具 会认为这是您正在寻找的正确方法,因为它可以让您具体了解您期望或想要的异常,因此您实际上会引发错误以查看它是否引发了正确的异常。然后你让nose 评估结果(在单元测试中加入尽可能少的逻辑!)

nose provides tools for testing exceptions (like unittest does).
Try this example (and read about the other tools at Nose Testing Tools

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]

I would think that this is the proper way that you were looking for because it lets you be specific about the exceptions that you expect or want. So you actually provoke the error to see that it raises the right exception. And then you let nose evaluate the result. (Put as little logic into the unit tests as possible!)

请爱~陌生人 2024-12-17 13:37:14

我强烈建议使用 nose.tools 中的 assert_raisesassert_raises_regexp,它们复制了 assertRaisesassertRaisesRegexp 来自 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)

使用 @raisestest5 将通过,但它会因错误的原因而通过。 test6 执行了更精细的测试,结果表明引发的 ValueError 不是我们想要的。

I strongly recommend using assert_raises and assert_raises_regexp from nose.tools, which duplicate the behavior of assertRaises and assertRaisesRegexp from unittest.TestCase. These allow using the same functionality as provided by unittest.TestCase in test suites that do not actually use the unittest.TestCase class.

I find that @raises is much too blunt an instrument. Here is code illustrating the problem:

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 passes, but not because foo has raised the exception we were expecting but because the code that sets up the data to be used by foo fails with the same exception. test4 shows how the test can be written using assert_raises to actually test what we mean to be testing. The problem on the first line will cause Nose to report an error and then we can rewrite the test so that that line so that we can finally test what we did mean to test.

@raises does not allow testing the message associated with the exception. When I raise ValueError, just to take one example, I usually want to raise it with an informative message. Here's an example:

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 which uses @raises will pass, but it will pass for the wrong reason. test6 performs a finer test which reveals that the ValueError raised was not the one we wanted.

旧时浪漫 2024-12-17 13:37:14
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 如果引发异常,则不会运行。此外,在 except 块运行完成后,执行不会再次重新进入 try 块,因此您不会遇到麻烦。

     ↓
(statements)
     ↓    exception
   (try) ↚──────────→ (except)
     ↓                   │
(statements) ←───────────┘
     ↓
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

The semantics of try/except imply that the flow of execution leaves the try block on an exception, so assert False will not run if an exception is raised. Also, execution will not re-enter the try block again after the except block is done running, so you shouldn't run into trouble.

     ↓
(statements)
     ↓    exception
   (try) ↚──────────→ (except)
     ↓                   │
(statements) ←───────────┘
     ↓
旧人九事 2024-12-17 13:37:14

我不知道为什么它还不在这里,但以另一种方式存在:

import unittest

class TestCase(unittest.TestCase):

    def testKeyError(self):
        d = dict()
        with self.assertRaises(KeyError):
            d[1]

I do not know why it is not here yet but exist one more way:

import unittest

class TestCase(unittest.TestCase):

    def testKeyError(self):
        d = dict()
        with self.assertRaises(KeyError):
            d[1]
南汐寒笙箫 2024-12-17 13:37:14

使用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(参考。如何使用nose的assert_raises?

Use 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)

Using try and catch in your tests seems like bad practice (most cases).

There's no specific documentation in nose because it's basically just a wrapper around unittest.TestCase.assertRaises (ref. How to use nose's assert_raises?)

迎风吟唱 2024-12-17 13:37:14

我不知道鼻子是什么,但是你尝试过在 except 子句后面使用“else”吗? IE

else:
    assert False

I don't know what nose is, but have you tried using 'else' after the except clause. I.e.

else:
    assert False
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文