python测试与调试 —— 在TestCase子类里验证相关的行为

less than 1 minute read

Published:

在Python中编写测试的最经典办法是使用内置的unittest模块。例如,这里有个定义在utils.py文件里的工具函数,我们想验证它能不能正确地处理各种输入数据。

utils.py

def to_str(data):
    if isinstance(data, str):
        return data
    elif isinstance(data, bytes):
        return data.decode('utf-8')
    else:
        raise TypeError('Must supply str or bytes, '
                        'found: %r' % data)

为了定义测试用例,需要再创建一个文件,将其命名为test_utils.py或utils_test.py。这个文件要把我们想确认的每一种行为都测试到。

utils_test.py

from unittest import TestCase, main
from utils import to_str

class UtilsTestCase(TestCase):
    def test_to_str_bytes(self):
        self.assertEqual('hello', to_str(b'hello'))

    def test_to_str_str(self):
        self.assertEqual('hello', to_str('hello'))

    def test_failing(self):
        self.assertEqual('incorrect', to_str('hello'))

if __name__ == '__main__':
    main()

执行utils_test.py文件,输出为:

F..
======================================================================
FAIL: test_failing (__main__.UtilsTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/wenyan/Desktop/utils_test.py", line 12, in test_failing
    self.assertEqual('incorrect', to_str('hello'))
AssertionError: 'incorrect' != 'hello'
- incorrect
+ hello


----------------------------------------------------------------------
Ran 3 tests in 0.000s

FAILED (failures=1)

可以看到有一项测试失败了。而且给出了调用堆栈。

测试用例需要安排到TestCase的子类中。在这样的子类中,每个以test开头的方法都表示一项测试用例。如果test方法在运行过程中没有抛出任何异常(assert语句所触发的AssertionError也算异常),那么这项测试用例就是成功的,否则就是失败。其中一项测试用例失败,并不影响系统继续执行TestCase子类里的其他test方法,所以我们最后能够看到总的结果,知道其中有多少项测试用例成功,多少项失败,而不是只要遇到测试用例失败,就立刻停止整套测试。

在修改了软件产品中的某个方法之后,我们可能想把针对该方法而写的测试用例迅速执行一遍,看自己改得对不对。在这种情况下,可以把TestCase子类的名称与test方法的名字直接写在原有的命令右边。

python utils_test.py UtilsTestCase.test_to_str_bytes

输出:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

我们还可以在test方法中指定断点,这样能够直接从此处激活调试器(debugger),以观察详细的出错原因。

TestCase类提供了一些辅助方法,可以在测试用例中做断言。例如,assertEqual方法可以确认两者是否相等,assertTrue可以确认Boolean表达式是否为True,此外还有很多以assert开头的方法。这些方法比内置的assert语句好用,因为它们可以把输入值与输出值详细打印出来,让我们准确地了解这项测试用例为什么会失败。下面我们在两个测试用例中分别用assert辅助方法与assert语句来做验证,以对比它们的效果。

from unittest import TestCase, main
from utils import to_str

class AssertTestCase(TestCase):
    def test_assert_helper(self):
        expected = 12
        found = 2 * 5
        self.assertEqual(expected, found)

    def test_assert_statement(self):
        expected = 12
        found = 2 * 5
        assert expected == found

if __name__ == '__main__':
    main()