Python单元测试中的多个异常和代码覆盖率

11

问题:

以下是被测试代码的人工示例:

from datetime import datetime

def f(s):
    try:
        date = s.split(":")[1]
        return datetime.strptime(date, "%Y%m%d")
    except (ValueError, IndexError) as e:
        # some code here
        raise

这是我目前拥有的一组测试:

from datetime import datetime
import unittest

from test_module import f

class MyTestCase(unittest.TestCase):
    def test_valid_date(self):
        self.assertEqual(f("1:20130101"), datetime(2013, 1, 1))

    def test_invalid_date(self):
        self.assertRaises(ValueError, f, "1:invalid")

测试通过,如果我使用--branch标志运行覆盖率,我将获得100%的行和分支覆盖率

$ coverage run --branch -m unittest test
..
----------------------------------------------------------------------
Ran 2 tests in 0.003s

OK
$ coverage report
Name            Stmts   Miss Branch BrPart  Cover
--------------------------------------------
test_module.py      7      0      0      0   100%
--------------------------------------------
TOTAL               7      0      0      0   100%

需要注意的是,当前测试仅检查两种情况——当没有异常抛出时和当引发 ValueError 异常时。

问题:

coverage是否有一种报告方式可以告诉我没有测试当IndexError被引发的情况吗?


你可以为 IndexError 编写一个不同的 except 块。我认为覆盖率只计算已执行的行。 - Håken Lid
2个回答

9

Coverage.py只能测量哪些执行路径(语句或分支)被运行。它无法跟踪使用的值,包括引发了哪些异常类型。

在我看来,你的选择有:

  1. 分开异常子句。在您展示的代码中,两个异常可能已经被单独的行引发了,虽然在您的实际代码中它们可能不那么容易分离。

  2. 不要担心这两个异常。你对这段代码的测试很可能会考虑许多不同的输入,以便运用不同的边界情况。Coverage.py无法帮助你区分它们,并确保你编写了足够的测试用例。使用其他标准来确定是否编写了足够的测试用例。


好的,非常感谢。祝你在PyCon的演讲顺利! - alecxe

2

我认为你可以尝试使用两个单独的except来处理这两种异常情况。这样,代码行覆盖率就能够显示出你已经测试了其中一个条件。

from datetime import datetime

def f(s):
    try:
        date = s.split(":")[1]
        return datetime.strptime(date, "%Y%m%d")
    except ValueError as e:
        # some code here
        raise
    except IndexError as e:
        # some code
        raise

如果您不想重复使用某些代码,您可以尝试使用函数来避免这种情况。

这绝对是一个选项,但我的好奇心仍然有一个问题,即覆盖率(或其他包)是否会确定未经测试的执行路径而不更改受测试代码。谢谢! - alecxe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接