AI工具教程

用 AI 为代码库补单元测试:从选函数到验证边界

AI智能摘要

开发者常借助 AI 为代码库补单元测试,但快速生成的测试可能仅重复现有实现,错误被一同掩盖。文章提出将 AI 融入可检查闭环:挑选职责明确、边界清晰的函数(如参数校验、数据转换、频繁调用且分支多),明确行为约定(None、空值、截断上限等),生成 pytest 测试后本地运行,并逐条审查断言是否覆盖真实需求,而非仅看覆盖率。最终,当函数违反约定时,这组测试能否可靠地失败?

— 此摘要由AI分析文章内容生成,仅供参考。

AI 可以很快补出一批单元测试,但“测试通过”不等于“测试有用”:如果断言只是重复当前实现,代码里的错误也可能被测试一起固定下来。更稳妥的做法,是把 AI 放进一个可检查的闭环:先挑函数、再说明行为、生成测试后本地运行,最后逐条审查断言是否覆盖了真实需求。

第一步:挑一个值得补测的函数

不要一上来就让 AI 扫完整个代码库。先找一个职责相对明确、输入和输出容易描述的函数,例如数据转换、参数校验、金额计算或状态判断。它最好能在较少外部依赖的情况下运行,这样失败时更容易定位原因。

可以优先考虑这些目标:

  • 有清晰的输入、输出或异常约定。
  • 存在容易漏掉的边界,例如空值、最小值、最大值、刚好越界。
  • 经常被调用,改动后可能影响多个功能。
  • 逻辑虽然不长,但规则多、分支多,人工回归容易漏测。

反过来,如果函数依赖数据库、网络请求、文件系统或复杂全局状态,先确认现有测试如何隔离这些依赖。必要时把目标缩小到其中一段纯逻辑,或先补齐可控的 mock;否则,生成的测试可能主要在处理环境,而不是验证函数行为。

第二步:先写清行为,再让 AI 生成

AI 能根据代码猜测意图,但代码本身不一定完整表达业务规则。提示词里应同时提供函数代码、相关类型或调用方式,以及你期望的行为。尤其要写明异常规则和边界:例如空值是返回默认值还是抛异常,最大值是允许还是截断,等于阈值时属于哪一侧。

下面以一个分页参数函数为例。它的约定是:None、无法转换的值和非正整数返回默认值;超过上限的值截断到上限。

纯文本
def normalize_page_size(value, default=20, maximum=100):
    if value is None:
        return default

    try:
        value = int(value)
    except (TypeError, ValueError):
        return default

    if value <= 0:
        return default

    return min(value, maximum)

可以把需求写成这样:

纯文本
请为下面的 Python 函数编写 pytest 单元测试。

行为约定:
- value 为 None 时返回 default。
- value 无法转换为整数,或转换后小于等于 0 时,返回 default。
- 1 到 maximum 之间(含边界)的值原样返回。
- 大于 maximum 时返回 maximum。
- 不修改生产代码;使用参数化测试组织重复用例。
- 只根据以上行为和调用者可观察到的结果编写断言,不测试内部实现细节。
- 如果代码行为与约定存在歧义,请先指出,不要自行补充业务规则。

函数代码:
[粘贴函数]

这里的关键不是提示词越长越好,而是把“输入—预期结果”说完整。若函数涉及副作用,还要说明哪些调用应发生、哪些不应发生;若依赖外部服务,则明确哪些依赖可以 mock,以及要验证的是调用结果还是业务输出。

第三步:运行测试,检查边界有没有落地

生成后先看代码,再运行,不要直接把整段结果提交。假设测试文件名为 test_pagination.py,本地可执行:

纯文本
python -m pytest -q test_pagination.py

一组基础用例可以这样写:

纯文本
import pytest

from pagination import normalize_page_size


@pytest.mark.parametrize(
    ("value", "expected"),
    [
        (None, 20),
        ("abc", 20),
        (0, 20),
        (-1, 20),
        (1, 1),
        (100, 100),
        (101, 100),
    ],
)
def test_normalize_page_size(value, expected):
    assert normalize_page_size(value) == expected

逐项核对时,别只问“有没有测试”,还要问“输入是否真的踩到边界”。例如 100 与 101 分别验证上限处和上限外;如果只测 50,就无法知道上限规则是否正确。对于 None、非法字符串、零和负数,也应确认它们对应的预期是否来自需求,而不是 AI 自己推测出来的。

如果项目已经有完整测试集,也要在目标测试通过后运行相关测试,必要时再运行全量测试:

纯文本
python -m pytest

测试命令和配置可能因项目使用的框架、虚拟环境或测试目录而不同,应优先遵循仓库现有说明。

第四步:审查断言,而不是只看覆盖率

代码覆盖率能说明哪些代码被执行过,却不能单独证明测试能区分正确行为和错误行为。下面几类测试尤其值得警惕:

  • 只断言结果“不是空值”或“没有报错”,没有验证关键结果。
  • 断言值直接由被测函数当前实现推导,换一种错误实现也能通过。
  • 只测试常见输入,没有覆盖需求明确提到的边界。
  • 为了让测试通过而修改预期值,却没有核对需求或调用方约定。
  • 测试依赖内部变量、私有方法或具体调用顺序,但这些细节并非对外契约。

一个实用的检查方法是设想一个小错误:比如把 value <= 0 改成 value < 0,或者把上限判断改成 maximum + 1。已有测试是否会失败?如果仍然全部通过,说明至少有一项行为没有被有效断言。变异测试工具可以进一步自动化这种检查;手动审查时,也可以先挑关键分支做简单的错误模拟,再恢复代码。

测试失败时,先分清是测试错还是代码错

失败不是立即改测试的理由。按顺序排查更省时间:

  1. 确认预期来源。 对照需求、接口约定或调用方行为,确认测试期待的是产品规则,而非提示词里随手补出的规则。
  2. 确认实际输入。 检查参数类型、默认值、测试夹具和 mock 返回值,避免测试传入的内容与设想不一致。
  3. 观察失败位置。 如果结果与明确约定不符,可能是生产代码缺陷;如果约定本身不清晰,先确认规则,不要让测试替业务做决定。
  4. 独立验证修改。 若修改生产代码,确认新测试确实捕捉到原问题;若修改测试,说明原断言为何不符合契约,并保留对边界行为的验证。

AI 生成的测试可能漏掉场景,也可能误解接口;人工审查仍然不可省。尤其当测试涉及金额、权限、并发、时间或数据持久化时,单元测试通常只验证局部逻辑,不能代替集成测试和业务验收。

把闭环变成日常流程

每次只让 AI 处理一个函数或一类行为:选目标、提供上下文和边界、生成测试、运行、检查断言,再根据失败修订。最终关注的不是新增了多少行测试,也不是覆盖率数字涨了多少,而是一个具体问题:当函数违反约定时,这组测试能不能可靠地失败?如果答案不确定,就回到输入输出和边界条件,补清行为后再生成或手动调整。

热门话题

52okp 是一名关注人工智能、开源软件与效率工具的技术内容创作者,长期实践 Stable Diffusion、ComfyUI、AI 智能体、MCP、Codex 和各类开源项目。通过实际安装、配置与测试,整理可复现的操作教程、问题排查方法和工具使用经验。

登录用户才能发表评论! 登录账户

取消回复

评论列表 (1条):

加载更多评论 Loading...

延伸阅读:

让大模型稳定返回 JSON:结构约束、校验与失败重试

大模型看似听话地返回 JSON,却常夹杂解释文字,甚至把数字写成字符串,直接 json.loads 就报错;更棘手的是失...

52okp
2026-09-29

Goose:不只是代码建议,这个开源 AI Agent 能直接帮你执行、编辑并测试工程任务

不少 AI 编程工具止步于代码建议,真正的工程任务还要读项目、改文件、运行命令并验证结果;让 Agent 直接操作虽能推...

52okp
2026-09-28
    返回顶部