1. 为纯函数编写首个单元测试
任务:给一个纯加法函数补上第一组测试
本节要测试的对象是一个无副作用的纯函数 add(a, b):它只依赖入参、只返回结果,不读写外部状态。测试框架选用 Python 标准库自带的 unittest,无需额外安装即可运行。我们要完成三件事:写出被测函数、用断言验证正常输入、用断言验证边界输入,并理解一个测试用例由“准备数据 → 调用被测函数 → 断言结果”构成。
被测函数与测试文件
把被测函数和测试代码放在同一目录下,先建立被测模块 calc.py:
# calc.py
def add(a, b):
"""返回 a 与 b 的和,无副作用。"""
return a + b
再建立测试文件 test_calc.py。测试类必须继承 unittest.TestCase,每个测试方法名以 test_ 开头,框架才会自动发现并执行它:
# test_calc.py
import unittest
from calc import add
class TestAdd(unittest.TestCase):
def test_add_two_positive_integers(self):
# 正常输入:两个正整数
result = add(2, 3)
self.assertEqual(result, 5)
def test_add_positive_and_negative(self):
# 正常输入:一正一负
result = add(10, -4)
self.assertEqual(result, 6)
def test_add_with_zero(self):
# 边界输入:加数为 0
self.assertEqual(add(0, 7), 7)
self.assertEqual(add(7, 0), 7)
def test_add_two_zeros(self):
# 边界输入:两个 0
self.assertEqual(add(0, 0), 0)
def test_add_floats(self):
# 正常输入:浮点数
self.assertAlmostEqual(add(0.1, 0.2), 0.3, places=7)
if __name__ == "__main__":
unittest.main()
测试用例的基本结构
每个 test_ 方法内部都遵循同一套三段式结构,这是单元测试最核心的骨架:
- Arrange(准备):确定输入值,例如
a = 2, b = 3。 - Act(执行):调用被测函数,例如
result = add(2, 3)。 - Assert(断言):用断言方法比对实际值与期望值,例如
self.assertEqual(result, 5)。
断言是测试的判定点:断言通过,该用例记为通过;断言失败,框架抛出 AssertionError 并标记该用例失败。常用断言方法如下表:
| 断言方法 | 判定含义 | 本例用途 |
|---|---|---|
assertEqual(a, b) | a 等于 b | 整数相加结果比对 |
assertNotEqual(a, b) | a 不等于 b | 验证错误结果不被误判为通过 |
assertAlmostEqual(a, b, places=n) | 四舍五入到 n 位后相等 | 浮点数相加结果比对 |
assertTrue(x) / assertFalse(x) | x 为真 / 为假 | 布尔型结果判定 |
浮点数不要直接用
assertEqual 比较,因为 0.1 + 0.2 在二进制浮点下并不精确等于 0.3,应使用 assertAlmostEqual 并指定精度。运行与结果判读
在测试文件所在目录执行:
python -m unittest test_calc.py -v
-v 会逐条打印用例名与结果。全部通过时输出形如 Ran 5 tests ... OK;若某条断言失败,输出会指出失败的用例名、断言行以及 AssertionError 的实际值与期望值差异,据此定位是被测函数逻辑错误还是期望值写错。
可执行检查清单
| 检查项 | 通过标准 |
|---|---|
测试类继承 unittest.TestCase | 类定义形如 class TestAdd(unittest.TestCase) |
| 测试方法命名 | 所有方法以 test_ 开头,框架能自动发现 |
| 正常输入覆盖 | 至少包含正整数、一正一负、浮点数三类 |
| 边界输入覆盖 | 至少包含加数为 0、两个 0 两类 |
| 浮点断言方式 | 使用 assertAlmostEqual 而非 assertEqual |
| 运行结果 | python -m unittest test_calc.py -v 输出 OK |
若把
add 改成 return a - b 再运行,test_add_two_positive_integers 会失败并报 AssertionError: -1 != 5。这说明断言确实在检验函数行为,而不是走过场——测试通过的前提是断言真的能失败。