如何断言发出特定异常

Sua*_*guo 0 python unit-testing

我有以下代码:

def foo(input):
    if not input.startswith("prefix"):
        raise Exception("input should start with prefix!")
    process(input)
Run Code Online (Sandbox Code Playgroud)

现在我想对这个功能进行单元测试; 什么时候input不启动"prefix"它实际上可以抛出异常.这是我的unittest代码:

def test_foo_invalud_type(self):
    self.assertRaises(Exception, foo, 999)
Run Code Online (Sandbox Code Playgroud)

这个测试代码的问题是:它捕获异常,但异常是'int' object has no attribute 'startswith',而不是input should start with prefix!.测试将通过,但这不是一个好的测试.我如何识别这两个例外?

jon*_*rpe 6

raise Exception是非常糟糕的形式 - 尝试在提出异常时更具体.使用这样的一般异常使得很难区分您尝试测试的两种不同情况.

在这种情况下,例如,我认为它ValueError是最合适的foo,因为它是正确的事物类型(字符串)但没有正确的值('prefix'开头没有).

def foo(input):
    if not input.startswith("prefix"):
        raise ValueError("input should start with prefix!")
    process(input)
Run Code Online (Sandbox Code Playgroud)

这使您可以区分输入错误的类型(AttributeError由于缺少.startswith*而引发输入):

def test_foo_invalid_type(self):  # note fixed typo in 'invalud'
    self.assertRaises(AttributeError, foo, 999)  # and more specific error
Run Code Online (Sandbox Code Playgroud)

并且输入的是错误的(ValueError为缺少'prefix'前缀显式引发):

def test_foo_invalid_value(self):
    with self.assertRaises(ValueError) as cm:
       foo('no prefix')
       self.assertEqual(
           cm.exception.args, 
           ('input should start with prefix!',),
       )
Run Code Online (Sandbox Code Playgroud)

请注意如何使用with上下文管理器表单assertRaises来访问错误本身.这允许您还检查是否为错误提供了正确的消息.


*你甚至可以考虑处理AttributeError 内部 foo,并提出TypeError改为.这似乎更适合"那个参数是错误的类型".