第 24 课 · 进阶 · 18 分钟

Python assert 测试

把预期行为变成可以反复运行的检查。

assert测试AssertionError边界情况

Maya 改了总价公式,得确认那张 18.75 的订单还没被改坏。一条 assert 一秒就能回答。什么时候 assert 是错的工具?

assert 语句会检查一个条件,条件为假时抛出 AssertionError。学习阶段,它是记录并验证预期的紧凑方式。它适合内部检查和测试,不适合校验不可信的用户输入。 你还会用表格驱动用例、断言预期异常,并用容差比较浮点数。

断言一个事实

assert add(2, 3) == 5 用可执行的形式写出预期结果。如果函数以后改成返回 6,程序会停在这一行。逗号后面的短消息可以说明哪里不符合预期。

def add(a, b):
    return a + b
assert add(2, 3) == 5
print("ok")

一个通过的检查

测试多个例子

少量断言就能覆盖正常输入和一个边界情况。最重要的行为放在前面。某个断言失败时,Python 会报告行号并停止当前测试,后面的检查不会执行。

def slugify(text):
    return text.strip().lower().replace(" ", "-")
assert slugify(" Hello World ") == "hello-world"
assert slugify("Python") == "python"
print("2 tests passed")

不止一个例子

捕获失败并查看原因

AssertionError 和其他异常一样。在小示例里捕获它,可以只打印消息而不显示整段 traceback。真实测试套件中应让测试运行器报告失败,而不是把失败藏起来。

def double(n):
    return n * 2
try:
    assert double(3) == 7, "double(3) should be 6"
except AssertionError as error:
    print(error)

读取失败消息

把用例放进表格

用 (输入, 预期) 列表把覆盖范围摆出来,每条断言也能保持短小。消息里用 repr(),失败时能看见准确的值。第一条失败的 assert 会停止运行,所以测试运行器一次只报告一个失败。

def slugify(text):
    return text.strip().lower().replace(" ", "-")

cases = [(" Hello World ", "hello-world"), ("Python", "python"), ("", "")]
for raw, expected in cases:
    actual = slugify(raw)
    assert actual == expected, f"{raw!r}: {actual!r} != {expected!r}"
print(len(cases), "cases passed")

一张表,一条清楚的失败消息

断言异常,而不是保持沉默

有些行为本来就应该报错。调用函数,只捕获预期异常,并断言它确实发生;else 分支负责抓“没有报错”的情况。真实测试套件里用 pytest.raises 或 unittest 的 assertRaises,不要用裸 try 把失败藏起来。

def parse_age(raw):
    value = int(raw)
    if value < 0:
        raise ValueError("age must be non-negative")
    return value

for raw in ["-1", "many"]:
    try:
        parse_age(raw)
    except ValueError as error:
        print(raw, type(error).__name__, error)
    else:
        print("no error", raw)

预期失败也是契约的一部分

浮点数用容差比较

二进制浮点会让 0.1 + 0.2 与 0.3 略有差异。要求精确相等的测试测的是表示方式,不是目标值。用 math.isclose 并写明容差;金额场景则比较整数分。

import math
total = 0.1 + 0.2
print(total == 0.3)
print(math.isclose(total, 0.3, rel_tol=1e-9))
assert math.isclose(total, 0.3, rel_tol=1e-9)
print("close enough")

在规定容差内测试值

本课要点

  • assert condition 在条件为假时抛出 AssertionError。
  • 逗号后面的字符串可以补充说明预期。
  • 断言用于测试和内部不变量,不用于用户输入校验。
  • Python 的 -O 参数会移除 assert,所以不能用它做安全校验。
  • 测试要有可见用例、明确的预期异常,以及对浮点数的容差。

常见错误

  • 用 assert 校验表单或密码。
  • 只测试顺利路径,跳过空值和边界输入。
  • 错误消息太模糊,没有说明预期是什么。
  • 用 assert 校验用户输入;-O 会把断言从运行中的程序移除。
轮到你

写一个 square(n),断言 square(4) == 16 和 square(0) == 0,然后打印 'ok'。

动手试试

改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。

浏览器内 Python本地运行 · 无需账号
首次运行会从 CDN 下载 Pyodide。修改后点击“运行代码”。

练习本课

12 道题,作答后立即看到解释。

查看 12 道题答案开始练习
← 日期与时间类型提示 →