首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >AI 写的测试覆盖率 90%,为什么上线还是崩?——测试质量的三个盲区

AI 写的测试覆盖率 90%,为什么上线还是崩?——测试质量的三个盲区

原创
作者头像
用户12475538
发布2026-05-11 13:11:20
发布2026-05-11 13:11:20
940
举报
文章被收录于专栏:与workbuddy合作与workbuddy合作

AI 写的测试覆盖率 90%,为什么上线还是崩?——测试质量的三个盲区

AI 写的测试有一种魔力:跑起来全绿,覆盖率报表漂亮,但线上该崩还是崩。这不是 AI 的问题——是"看起来在测"和"真的在测"之间的鸿沟。


一个让我怀疑人生的测试

AI 给我生成了一个用户认证模块,顺便写了测试。跑一下——11 个测试全过,覆盖率 92%。完美。

两周后,线上出了一个 bug:用户用特殊字符组成的密码可以绕过验证。回看测试代码,11 个测试里没有一个测试过特殊字符输入。

所有测试都在测"正常情况"和"AI 能想到的异常"。 真正的 bug 藏在 AI 没想到的边界里。


盲区一:只测了"AI 能想到的"

AI 的典型测试

代码语言:python
复制
def test_create_user():
    # ✅ 正常情况
    user = create_user("test@example.com", "Password123")
    assert user.email == "test@example.com"

    # ✅ 显而易见的异常
    with pytest.raises(ValueError):
        create_user("", "Password123")  # 空邮箱

    with pytest.raises(ValueError):
        create_user("test@example.com", "")  # 空密码

测试全过。但这个模块上线后,暴露出了一堆问题:

代码语言:python
复制
# ❌ 这些情况 AI 的测试没覆盖
create_user("test@example.com", "ab")          # 密码太短
create_user("test@example.com", "a" * 1000)     # 密码太长(缓冲区溢出?)
create_user("test@example.com", "<script>")     # XSS 注入
create_user("test@example.com", "  Password1 ") # 前后空格
create_user("test@example.com", "密码123")      # Unicode 字符
create_user("a" * 300 + "@example.com", "ok")  # 超长邮箱
create_user("test@", "ok")                      # 无效邮箱格式

AI 的测试覆盖了"它被训练见过的异常模式"。 但训练数据里的代码示例,很少包含"密码前后有空格"这种真实世界的脏数据。

补救方法:脏数据注入

代码语言:python
复制
# 每次 AI 写完测试后,手动补充一个"脏数据集"
DIRTY_INPUTS = [
    ("空字符串", ""),
    ("仅空格", "     "),
    ("超长字符串", "a" * 10000),
    ("SQL注入尝试", "'; DROP TABLE users; --"),
    ("XSS尝试", "<script>alert(1)</script>"),
    ("Unicode特殊字符", "𝕴𝖓𝖘𝖊𝖗𝖙 𝖒𝖆𝖌𝖎𝖈"),
    ("零宽字符", "hello\u200bworld"),
    ("emoji", "😀" * 100),
    ("前后空格", "  value  "),
    ("换行符注入", "value\n\r"),
]

@pytest.mark.parametrize("desc,dirty_input", DIRTY_INPUTS)
def test_against_dirty_input(desc, dirty_input):
    # 你的函数遇到脏数据应该优雅降级,不是崩溃
    result = your_function(dirty_input)
    assert result is not None
    assert not isinstance(result, Exception)

AI 不会自动生成脏数据测试,因为它训练时看到的代码是干净的。 你得自己维护一个脏数据集,每次写测试时跑一遍。


盲区二:测试了代码,没测试"代码应该做的事"

AI 的典型测试

代码语言:python
复制
def test_transfer_money():
    alice = create_account(balance=100)
    bob = create_account(balance=50)

    transfer(alice.id, bob.id, 30)

    assert alice.balance == 70   # Alice 扣了 30
    assert bob.balance == 80     # Bob 收了 30

这测试跑过了,但没测到这些关键问题:

代码语言:python
复制
# 真实业务场景需要验证的
def test_transfer_money_REAL():
    # ❌ AI 没测:并发转账
    # 两个线程同时给 Bob 转钱,总金额对吗?

    # ❌ AI 没测:余额不足
    with pytest.raises(InsufficientBalance):
        transfer(alice.id, bob.id, 200)

    # ❌ AI 没测:转给自己
    with pytest.raises(InvalidTransfer):
        transfer(alice.id, alice.id, 10)

    # ❌ AI 没测:金额为 0 或负数
    with pytest.raises(InvalidAmount):
        transfer(alice.id, bob.id, 0)
    with pytest.raises(InvalidAmount):
        transfer(alice.id, bob.id, -50)

    # ❌ AI 没测:转账后的幂等性
    # 同一条转账指令执行两次,不应该扣两次钱
    tid = transfer(alice.id, bob.id, 30)
    transfer(alice.id, bob.id, 30, idempotency_key=tid)  # 应该被忽略
    assert alice.balance == 70  # 没有多扣

AI 测试了 happy path。人类需要测试 sad path、weird path、和"这不可能发生但确实发生了"的 path。

补救方法:每写一个函数前,先列违规场景

代码语言:python
复制
# 不是"写测试",是"写违规场景清单"
def list_violations(func_name):
    """
    转账函数可能违规的场景:
    1. 源账户不存在
    2. 目标账户不存在
    3. 余额不足
    4. 金额 <= 0
    5. 转给自己
    6. 重复转账(幂等性)
    7. 并发转账(竞争条件)
    8. 转账过程中账户被冻结
    """
    pass

先列违规场景,再用 AI 逐个生成测试。 AI 擅长"把一个具体场景变成测试代码",不擅长"从零思考哪些场景需要测试"。


盲区三:测试有依赖,但依赖的 mock 不真实

AI 的典型测试

代码语言:python
复制
def test_send_notification():
    mock_email = Mock()
    mock_email.send.return_value = True

    result = notify_user(user, "welcome", email_service=mock_email)

    assert result.success
    mock_email.send.assert_called_once()

mock 了 email_service,验证了它被调用了。完美。

上线后:邮件服务换了 API,send 方法的签名变了,mock 的断言错了——但测试照样过,因为 mock 不管签名。

补救方法

代码语言:python
复制
# ✅ 测试 mock 的行为,也要验证 mock 的参数
def test_send_notification_REAL():
    mock_email = Mock()
    mock_email.send.return_value = True

    result = notify_user(
        user,
        template="welcome",
        email_service=mock_email
    )

    # 不只是"被调用了",而是"用正确的参数调用了"
    call_args = mock_email.send.call_args
    assert call_args.kwargs["to"] == user.email
    assert call_args.kwargs["subject"] is not None
    assert call_args.kwargs["subject"] != ""  # 主题不为空
    assert "welcome" in call_args.kwargs["template"]
    assert call_args.kwargs["timeout"] == 30  # 设了超时

mock 验证的颗粒度:不仅要验证"调了",还要验证"调对了"。

另外,定期做一次"去 mock 化"测试:

代码语言:python
复制
# ✅ 关键路径的集成测试(不 mock 外部依赖)
@pytest.mark.integration
def test_send_notification_integration():
    # 用真实邮件服务(测试环境),验证端到端
    email_service = RealEmailService(env="test")
    result = notify_user(test_user, "welcome", email_service=email_service)
    assert result.success

AI 写测试的正确姿势

不是"帮我写测试",而是分四步:

代码语言:python
复制
# 第一步:你列违规场景(AI 帮你扩展)
你:"这个转账函数可能违规的场景有哪些?"
AI:[列出 8 个场景]
你:"补充:并发转账、冻结账户的情况"

# 第二步:AI 逐个生成测试代码
你:"按上面 10 个场景,生成测试,每个测试独立"

# 第三步:你注入脏数据
你:"在这些测试的基础上,对所有字符串参数跑一遍脏数据集"

# 第四步:AI 补充边界断言
你:"每个测试加断言验证返回值的字段完整性"

一个检查清单

拿到 AI 写的测试,问三个问题:

代码语言:markdown
复制
[ ] 脏数据过了吗?
    → 空字符串、超长输入、注入尝试、特殊字符

[ ] 违规场景过了吗?
    → 余额不足、重复操作、并发访问、资源不存在

[ ] Mock 验证充分吗?
    → 不只"被调用了" → "用正确的参数和超时调用了"

三道都过,才算一份合格的测试——不管覆盖率报表上写的是多少。


本文所有示例均已脱敏处理。你的 AI 写的测试上线后崩过吗?崩在哪?评论区来比惨。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AI 写的测试覆盖率 90%,为什么上线还是崩?——测试质量的三个盲区
    • 一个让我怀疑人生的测试
    • 盲区一:只测了"AI 能想到的"
      • AI 的典型测试
      • 补救方法:脏数据注入
    • 盲区二:测试了代码,没测试"代码应该做的事"
      • AI 的典型测试
      • 补救方法:每写一个函数前,先列违规场景
    • 盲区三:测试有依赖,但依赖的 mock 不真实
      • AI 的典型测试
      • 补救方法
    • AI 写测试的正确姿势
    • 一个检查清单
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档