编写第一个单元测试:断言机制、测试用例结构、执行与结果
好,咱们正式动手写第一个单元测试。这章我带你从零搭一个最简单的测试,把断言、用例结构、运行结果这三个核心点吃透。你可能会觉得“不就写个assert嘛”,但实际项目里,断言的选型、用例的编排、结果的解读,坑并不少。
断言机制 —— 测试的灵魂
断言(assert)说白了就是“拍胸脯保证”:我断言这个值等于那个值,如果不对,测试就红。Python 的 unittest 内置了一整套断言方法,我习惯把它们分成三类:
- 值断言:判断相等、不等、是否为空
- 布尔断言:判断真假
- 异常断言:判断是否抛出了期望的异常
| 断言方法 | 作用 | 我用它做什么 |
|---|---|---|
assertEqual(a, b) |
检查 a == b | 验证函数返回值是否和预期一致 |
assertTrue(x) |
检查 x 为 True | 判断条件是否成立,比如列表是否为空 |
assertRaises(SomeException, func, arg) |
检查调用 func(arg) 时抛出 SomeException | 测试异常处理逻辑,我在这上面吃过亏,一会说 |
小技巧:
assertEqual 在比较浮点数时一定用 assertAlmostEqual,否则你可能会被精度问题烦死。你想想,0.1 + 0.2 在计算机里根本不是 0.3。
还记得我第一次写单元测试,直接用 assertEqual(result, 0.3),结果死活不通过。后来才发现浮点比较需要容差。这个坑我记忆犹新。
测试用例结构 —— 四步法
一个规范的测试用例,我建议按以下结构组织:
- 准备(Setup):创建测试所需的对象、数据
- 执行(Exercise):调用被测方法
- 断言(Assert):验证结果
- 清理(Teardown):释放资源(可选)
看看这个实际的例子,我们测试一个简单的计算器加法:
import unittest
class TestCalculator(unittest.TestCase):
def setUp(self):
# 准备:创建一个计算器实例
self.calc = Calculator()
def test_add_two_positive_numbers(self):
# 执行:调用 add 方法
result = self.calc.add(3, 5)
# 断言:期望结果是 8
self.assertEqual(result, 8)
def tearDown(self):
# 清理:这里不需要,但习惯性写上
pass
if __name__ == '__main__':
unittest.main()
你发现没有,setUp 和 tearDown 会被每个测试方法自动调用。这样做的好处是每个测试都是独立的,互不干扰。我曾经在项目里见过有人在 setUp 里初始化数据库连接,结果测试跑完后连接没关闭,导致后续测试超时——tearDown 就该干这个事。
注意:不要在测试方法里写
if __name__ == '__main__' 以外的全局代码,否则测试加载时可能产生副作用。曾经有个同事把数据库迁移脚本写在测试文件顶层,每次运行测试都重新建表,数据全没了……嗯,教训。
执行与结果解析
测试写好了,怎么跑?通常两种方式:
- 命令行:
python -m unittest test_module.py - IDE 按钮:PyCharm 等集成环境直接点绿色三角形
运行后会看到类似这样的输出:
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
. 表示一个测试通过,F 表示失败,E 表示出错(代码异常)。如果看到 FAILED (failures=1),说明断言没通过。我处理这类问题的习惯是:先看是哪一行断言失败,再对比实际值和期望值。
你看,就这么简单。但实际项目里,测试结果可能上百条,你需要学会快速定位。我通常会加上 -v 参数(verbose),它会打印每个测试的名字和结果:
python -m unittest -v test_calculator.py
输出变成:
test_add_negative_numbers (test_calculator.TestCalculator) ... ok
test_add_two_positive_numbers (test_calculator.TestCalculator) ... ok
test_add_zero (test_calculator.TestCalculator) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
这就能一眼看到哪个测试通过了。如果你看到 FAIL,后面会跟着具体的断言失败信息,比如“3 != 5”,定位问题非常快。
核心总结:断言是测试的裁判,用例结构是测试的骨架,执行结果是测试的成绩单。三者缺一不可。