跳到正文
效率工具实用笔记关于本站
效率工具

给计算函数写一个小测试:unittest入门

约 9 分钟阅读AI 辅助整理

用自建的加法函数演示 unittest 入门:继承 TestCase、用 assertEqual 核对返回值、用 unittest.main() 作为测试入口,并说明测试方法命名和运行方式。

本文要点

  1. 测试代码放在单独的文件里

  2. 每个测试的核心是调用断言方法

  3. 文件末尾那段 if __name__ == '__main__': unittest.main() 是测试入口

给计算函数写一个小测试:unittest入门的知识要点示意图
依据已审核正文摘取要点制作的示意图,非实物照片;请结合正文中的条件与范围阅读。要点摘自已审核正文;完整条件与说明见正文。查看高清图解 ↗
本文目录

先明确要测什么:一个自建的纯函数

假设你在练习目录里写了一个小工具,里面有一个负责计算的小函数,你希望改动它之后能快速确认结果没变。这就是单元测试要解决的问题。

按 unittest 文档的说明,测试用例(test case)是测试的独立单元,它检查一组特定输入是否得到某个特定响应;unittest 提供 TestCase 基类,用来创建新的测试用例。

本文只处理一个自建场景:一个只做加法、不读写文件、不访问网络的纯函数,给它写一个测试。所有文件名和内容都是练习用的自建示例,不是对真实项目的记录。动手前建议在练习目录里新建文件,例如 exercise/calc.py 和 exercise/test_calc.py,不要拿正在用的工具直接改。

先写被测试的函数。它接收两个数,返回它们的和:

# exercise/calc.py
def add(a, b):
    return a + b

这个函数没有副作用,输入确定,输出就确定,适合作为第一个测试对象。

测试文件怎么写:继承 TestCase,方法名以 test 开头

测试代码放在单独的文件里。按文档的说明,把测试代码放在独立模块有几个好处:测试模块可以单独从命令行运行,测试代码更容易与发布代码分开,也不容易被顺手改掉去迁就被测代码。

测试用例通过继承 unittest.TestCase 来创建。每个具体测试写成方法,方法名必须以 test 开头——文档明确说,这个命名约定告诉测试运行器哪些方法代表测试。

自建示例:

# exercise/test_calc.py
import unittest

from calc import add


class TestAdd(unittest.TestCase):

    def test_add_positive(self):
        self.assertEqual(add(1, 2), 3)

    def test_add_zero(self):
        self.assertEqual(add(0, 5), 5)


if __name__ == '__main__':
    unittest.main()

这里有两个测试方法,名字都以 test 开头。如果写成 check_add 或 add_test,运行器不会把它当成测试,这一点是初学者最容易踩的坑。

assertEqual 检查什么:相等就通过,不等就报失败

每个测试的核心是调用断言方法。文档把 assertEqual(first, second, msg=None) 描述为:检查 first 和 second 是否相等,如果两个值不相等,测试失败。

所以 self.assertEqual(add(1, 2), 3) 这行的读法是:把 add(1, 2) 的实际返回值放在第一个位置,把你预期的结果 3 放在第二个位置。两者相等,这条测试通过;不相等,这条测试失败。

文档还说明,当 first 和 second 类型完全相同、且属于 list、tuple、dict、set、frozenset、str 等类型时,会调用针对该类型的相等比较函数,生成更有用的默认错误信息。对数字比较来说,你得到的就是一条指出两个值不等的失败信息。

为什么用 assertEqual 而不是直接写 assert:文档解释,使用这些断言方法而不是 assert 语句,是为了让测试运行器能汇总所有测试结果并生成报告。用 assert 语句,第一个失败就可能中断后续检查。

assertEqual 还接受第三个参数 msg,用来在失败时给出自定义说明。文档说明所有断言方法都接受 msg 参数,指定后会在失败时作为错误信息使用。例如可以写成 self.assertEqual(add(1, 2), 3, '1 加 2 应为 3')。

测试入口:unittest.main() 做了什么

文件末尾那段 if __name__ == '__main__': unittest.main() 是测试入口。文档说明,unittest.main() 为测试脚本提供一个命令行接口。

它的作用是:当这个文件被直接运行时,收集本模块里的测试用例并执行。文档在讲测试套件时也提到,多数情况下调用 unittest.main() 就会做正确的事——自动收集模块里的测试用例并执行。

运行方式有两种,都来自文档的命令行接口说明:

1. 直接运行测试文件:

python exercise/test_calc.py

2. 用 unittest 模块从命令行运行:

python -m unittest exercise/test_calc.py

文档说明,测试模块也可以用文件路径指定,路径会被转换成模块名(去掉 .py,把路径分隔符换成点)。前提是这个文件仍然可以作为模块导入。

加 -v 参数会输出更详细的结果,文档给出的示例是 python -m unittest -v test_module。

需要说明的是:本文不展示任何实际运行输出,因为输出内容取决于你机器上的 Python 版本和实际代码。文档中给出的输出样式只是示例,不是本文的运行记录。

测试方法名决定执行顺序,别依赖先后关系

写多个测试方法时,有一个容易忽略的规则:文档说明,各个测试的运行顺序由测试方法名按字符串的内置排序决定。

这意味着 test_add_positive 和 test_add_zero 谁先跑,取决于名字的字母顺序,而不是你写在文件里的先后位置。所以每个测试方法都应该自包含,不依赖另一个测试先跑过。文档在讲测试用例时也强调,TestCase 实例的测试代码应当完全自包含,可以单独运行,也可以和任意数量的其他测试用例组合运行。

对本文这个加法函数来说,两个测试方法互不依赖,所以顺序不影响结果。但如果你以后写的测试需要先准备数据,就要用 setUp()。文档说明 setUp() 会在每个测试方法调用前立即执行,用来准备测试夹具(test fixture);如果 setUp() 抛出异常,框架会认为该测试发生错误,测试方法不会被执行。

本文的纯函数不需要准备数据,所以省略 setUp(),等被测对象开始依赖外部状态时再引入。

测试失败时先看哪几处:命名、导入和预期值

如果运行后没有得到预期结果,可以按下面几处依次检查。以下排查顺序是编辑整理的,不是对某个具体报错的记录。

1. 方法名有没有以 test 开头。不以 test 开头的方法不会被当作测试,运行器会直接跳过它。

2. 测试文件能不能导入被测模块。上面示例用的是 from calc import add,这要求 calc.py 在测试运行时能被找到。文档在讲测试发现时提醒,测试文件必须是可从项目顶层目录导入的模块或包。如果导入路径不对,会先报导入错误,而不是断言失败。

3. 预期值写在哪一侧。assertEqual 的第一个参数是实际值,第二个是预期值。写反了不会改变通过与否,但失败信息读起来会别扭。

4. 是不是把断言写成了 assert 语句。用 assert 语句时,测试运行器无法汇总结果,文档建议改用断言方法。

5. 有没有在测试方法里返回值。文档说明,从测试方法返回非 None 值的行为在 3.11 版本起已被弃用,测试方法应当只做检查,不返回结果。

这套做法的边界:只覆盖纯函数,不替代其他检查

本文演示的测试只针对一个自建的纯函数,适用范围有限。

第一,它只检查你写下的那几组输入输出。文档把测试用例描述为检查一组特定输入是否得到特定响应,所以它证明的是这几组输入下的行为,不是函数在所有输入下都正确。

第二,本文没有涉及 setUp()、tearDown()、测试套件、跳过测试、子测试等 unittest 的其他功能。文档说明这些功能存在,但本文的纯函数场景用不到,所以不展开。

第三,本文不涉及测试发现(test discovery)。文档说明,不带参数运行 python -m unittest 会启动测试发现,它要求测试文件是可从项目顶层目录导入的模块或包。本文用的是直接指定文件路径的方式,避开了这个前提。

第四,本文所有文件都是自建练习示例,路径写作 exercise/calc.py 和 exercise/test_calc.py。实际操作时请放在单独的练习目录,不要拿正在使用的工具直接改。

参考资料为 Python 官方 unittest 文档,其中关于 TestCase、assertEqual、unittest.main()、测试方法命名约定和运行顺序的说明,是本文事实部分的依据。

观看知识要点视频 18 秒

18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。

参考资料

以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。

请结合原始资料的适用条件阅读。

返回效率工具 →