Python 单元测试:用 unittest 检查时长解析的正常与边界输入
为学习时长解析函数编写表驱动测试,覆盖合法输入、边界、格式错误和类型错误,掌握 subTest 与 assertRaises 的使用方法。
本节目标
为学习时长解析函数编写表驱动测试,覆盖合法输入、边界、格式错误和类型错误,掌握 subTest 与 assertRaises 的使用方法。
知识与使用场景
前面的示例通过观察终端输出验证功能。单元测试把预期写成可重复执行的检查,代码修改后可以再次运行,确认契约没有被意外破坏。本节复制时长解析函数到同一文件,保证初次练习不依赖其他章节的文件位置。
测试分成三组:可解析的文本、应拒绝的文本、不符合输入类型的对象。对每组多个输入使用 subTest,让失败报告包含具体值。断言异常类型能检查失败方式,而不只是检查程序有没有输出一行文字。
测试重点是对外行为:返回多少分钟、哪些输入被拒绝。不要让测试照抄函数内部每一步,也不要为凑覆盖率只测试同一个普通数字。0、1440、负数和刚超过上限更能揭示边界判断错误。
完整示例
import unittest
def parse_minutes(text):
if not isinstance(text, str):
raise TypeError("时长必须以文本提供")
try:
value = int(text.strip())
except ValueError as exc:
raise ValueError("时长必须是整数文本") from exc
if not 0 <= value <= 1440:
raise ValueError("时长必须在0到1440之间")
return value
class ParseMinutesTests(unittest.TestCase):
def test_valid(self):
for raw, expected in [("0", 0), (" 45 ", 45), ("1440", 1440)]:
with self.subTest(raw=raw):
self.assertEqual(parse_minutes(raw), expected)
def test_invalid_text(self):
for raw in ["", "12.5", "-1", "1441"]:
with self.subTest(raw=raw):
with self.assertRaises(ValueError):
parse_minutes(raw)
def test_invalid_type(self):
for raw in [None, 20, True]:
with self.subTest(raw=raw):
with self.assertRaises(TypeError):
parse_minutes(raw)
if __name__ == "__main__":
unittest.main(verbosity=2)预期结果
OK运行与逐步理解
保存为 test_minutes.py。使用选定的解释器运行 test_minutes.py,或在同目录运行 python -m unittest -v test_minutes,其中 python 应替换为自己的虚拟环境解释器路径。
执行器会运行三个 test 开头的方法,内部合计检查 10 组输入。正常结果包含各方法的 ok、Ran 3 tests 和末尾 OK,耗时与模块前缀会因运行方式不同而变化。不要误把 10 组输入当成报告必须显示的 10 个测试方法。
真实项目可将 parse_minutes 放到 minutes.py,再由测试文件 from minutes import parse_minutes。保持导入时没有交互输入或文件写入,测试才容易自动执行。输出到标准错误流是 unittest 的常见行为,不等于测试失败,应同时看退出码和测试结果。
常见问题与边界
- assertRaises 必须围住实际调用,不能只传一个已计算好的返回值。
- 测试文件不要命名为 unittest.py,否则可能遮蔽标准库。
- 全部通过只能说明已覆盖场景满足预期,不代表任意输入都正确。
- 测试要能发现错误;可以临时把范围上限改成 1439,确认 1440 用例失败后再恢复。
动手练习
给有效输入增加 " +30 ",给无效文本增加 "1440.0",运行后说明结果;临时把下限比较改成 0 < value 再运行。
参考答案
带正号和空白的整数文本按当前契约应返回 30;1440.0 含小数点,应抛 ValueError。把下限改为严格大于零后,"0" 子用例失败,说明测试确实保护了合法边界。验证后恢复原实现再提交。
文章回复
0 条公开回复