AI 写的测试有一种魔力:跑起来全绿,覆盖率报表漂亮,但线上该崩还是崩。这不是 AI 的问题——是"看起来在测"和"真的在测"之间的鸿沟。
AI 给我生成了一个用户认证模块,顺便写了测试。跑一下——11 个测试全过,覆盖率 92%。完美。
两周后,线上出了一个 bug:用户用特殊字符组成的密码可以绕过验证。回看测试代码,11 个测试里没有一个测试过特殊字符输入。
所有测试都在测"正常情况"和"AI 能想到的异常"。 真正的 bug 藏在 AI 没想到的边界里。
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", "") # 空密码测试全过。但这个模块上线后,暴露出了一堆问题:
# ❌ 这些情况 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 的测试覆盖了"它被训练见过的异常模式"。 但训练数据里的代码示例,很少包含"密码前后有空格"这种真实世界的脏数据。
# 每次 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 不会自动生成脏数据测试,因为它训练时看到的代码是干净的。 你得自己维护一个脏数据集,每次写测试时跑一遍。
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这测试跑过了,但没测到这些关键问题:
# 真实业务场景需要验证的
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。
# 不是"写测试",是"写违规场景清单"
def list_violations(func_name):
"""
转账函数可能违规的场景:
1. 源账户不存在
2. 目标账户不存在
3. 余额不足
4. 金额 <= 0
5. 转给自己
6. 重复转账(幂等性)
7. 并发转账(竞争条件)
8. 转账过程中账户被冻结
"""
pass先列违规场景,再用 AI 逐个生成测试。 AI 擅长"把一个具体场景变成测试代码",不擅长"从零思考哪些场景需要测试"。
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 不管签名。
# ✅ 测试 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 化"测试:
# ✅ 关键路径的集成测试(不 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 帮你扩展)
你:"这个转账函数可能违规的场景有哪些?"
AI:[列出 8 个场景]
你:"补充:并发转账、冻结账户的情况"
# 第二步:AI 逐个生成测试代码
你:"按上面 10 个场景,生成测试,每个测试独立"
# 第三步:你注入脏数据
你:"在这些测试的基础上,对所有字符串参数跑一遍脏数据集"
# 第四步:AI 补充边界断言
你:"每个测试加断言验证返回值的字段完整性"拿到 AI 写的测试,问三个问题:
[ ] 脏数据过了吗?
→ 空字符串、超长输入、注入尝试、特殊字符
[ ] 违规场景过了吗?
→ 余额不足、重复操作、并发访问、资源不存在
[ ] Mock 验证充分吗?
→ 不只"被调用了" → "用正确的参数和超时调用了"三道都过,才算一份合格的测试——不管覆盖率报表上写的是多少。
本文所有示例均已脱敏处理。你的 AI 写的测试上线后崩过吗?崩在哪?评论区来比惨。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。