收益:
- 能够使用 AI 进行单元测试、边缘案例和覆盖差距分析
- 能够根据规范打印测试期望,而不是代码的当前行为
- 能够测试测试是否确实通过注入错误进行保护
编写测试是大多数开发人员推迟的最有价值的任务之一。一个好的测试套件可以证明代码按预期工作,并且是未来更改的生命线。问题在于编写测试是重复且耗时的——这正是人工智能擅长的工作。但有一个问题:人工智能经常测试代码的现有行为,而不是它应该有的行为。管理这种差异是本单元的本质。
在本单元中,您将学习单元测试(单独测试某个功能的测试)、边缘情况测试以及使用 AI 生成测试数据;缩小测试覆盖率的差距;以及为什么盲目信任人工智能测试是危险的。
测试的两个方面:修复行为与验证
测试可以服务于两个不同的目的。第一个是验证:它测试代码是否正确,是否符合规范。第二个是回归保护:它冻结了代码今天的行为,因此如果明天有人不小心更改了它,测试将中断并发出通知。
人工智能非常擅长后者;它查看代码并生成测试“它现在正在做什么”的案例。但如果代码从一开始就是错误的,人工智能可以将错误行为识别为“正确”。因此,您必须检查 AI 生成的每个测试的断言:“代码返回 42,测试期望 42”并不意味着 42 是正确答案。
注意:如果AI通过测试,并不意味着代码“可以工作”;它只是意味着“它的行为符合人工智能的预期”。您可以通过查看规范来确定期望是否正确。
一步一步:使用 AI 编写稳健的测试
- 给出规范,而不仅仅是代码。如果添加信息“这个函数应该这样做”,AI就可以写出正确的期望;如果您只提供代码,它将测试当前的行为。
- 询问边缘情况。空、null、零、负、太大、格式错误、并发——明确声明脱离快乐路径。
- 指定测试框架和风格。 “使用 pytest”、“安排-行动-断言模式”、“让每个测试测试一件事”等。
- 检查期望(断言)。与每个断言检查正确值的规范进行比较。
- 缩小范围上的差距。给出现有的测试并询问“哪些分支和案例尚未测试?”让你问;然后验证产生的附加测试。
三个迷你箱
案例 1 — 覆盖率从 52% 提高到 85%。某一业务模块的测试覆盖率为52%。该团队将现有的测试提供给人工智能,让它列出未经测试的分支并为它们生成测试。经过人工审核,覆盖率提高到 85%;在此过程中,AI 在之前从未测试过的 bug 分支中发现了一个实际的 bug(返回错误错误代码的路径)。
案例 2——错误的期望固定陷阱。货币舍入函数实际上是错误的;它不是将 2.675 四舍五入为 2.67,而是四舍五入 2.67 而不是 2.68。 AI 查看了代码并编写了断言 round_money(2.675) == 2.67——将错误冻结为“true”。当开发人员阅读规范时,他纠正了预期并发现了真正的错误。测试规则,而不是代码,才产生了差异。
情况 3 — 边缘状态爆炸。当要求 AI 仅提供日期范围函数的“边缘情况”时;它产生了开始=结束、反向间隔、闰年2月29日、不同时区和零间隔等8种情况。其中两个(反向间距和闰年)实际上导致了错误。通常会跳过手动考虑这些情况;人工智能在这里成为了“边缘案例头脑风暴”的合作伙伴。
四个可复制模板
基于规范的测试生成:
角色:编写测试的开发人员。框架:{{pytest/JUnit/Jest...}}。函数应该做什么(规范):{{rule}}为以下函数编写测试。根据规范编写期望,而不是代码的当前输出。快乐路径 + 添加至少 4 个边缘情况。让每个测试测试一件事,使用描述性名称。 {{功能}}
边缘案例头脑风暴:
列出测试此函数时应尝试的边缘/失败情况(空、空、断点、格式错误、并发、外部错误)。对于每种情况:输入、预期行为。不要编写代码,只需列出。{{function}}
覆盖差距分析:
以下是功能和可用的测试。哪些分支、条件和案例没有经过测试?列出缺陷并仅针对缺陷编写新的测试。不要重复现有的。功能:{{function}}测试:{{existing_tests}}
测试数据/模拟对象生成:
为{{function/service}}测试生成真实的测试数据:分别是有效样本、边界样本和无效样本。建议外部依赖项 {{X}} 的简单模拟行为。使用真实的机密数据/PII;生成虚假数据。
弱提示/强提示
弱:“为此函数编写一个测试。”
强:“使用pytest。函数apply_discount(total,percent) - 规则:折扣必须为0%–30%,超出范围应抛出ValueError,结果应四舍五入至小数点后两位。按此规则(而不是代码)写入期望。快乐路径+这些边缘情况:0%,30%,31%(错误),负数,总计= 0。[代码]”
他给出了强发布规则,并表示“根据规则编写期望,而不是代码”;这一句话关闭了人工智能修复不当行为的陷阱。
测试类型
人工智能贡献
人为控制
快乐的道路单元测试
快速骨架
预期是否正确?
边缘情况
广泛的头脑风暴
剔除不相关的内容
范围填补空白
查找跳过的分支
确认意义
测试数据/模拟
产生真实的样品
无 PII,真实控制
测试管理质量,而不是保证质量
高测试覆盖率给人信心,但也可能产生误导:100% 覆盖率意味着“每行都运行”,而不是“每行都是正确的”。利用人工智能扩大覆盖范围很容易;真正的价值在于写下有意义的期望。测试的价值在于它能够在代码被破坏时进行破坏并发出警报。这就是为什么人工智能生成的测试基于“代码更改时真的会崩溃吗?”这个问题。用问题来测试一下;故意打破一条线并看到测试中断(突变想法)是测试有效的证明。
提示:要查看 AI 编写的测试是否有效,请在代码中创建一个小错误(例如,将 + 更改为 -),然后查看测试是否会中断。如果它没有破裂,那么该测试就无法保护您。
常见错误
- 要求测试而不给出规则。该模型冻结当前行为;将错误修复为“true”。
- 不阅读就接受期望。如果您不检查断言是否检查正确的值,则测试会产生误导。
- 只是测试快乐之路。真正的错误存在于边缘;明确询问边缘情况。
- 将范围误认为目的。高百分比并不能保证行为正确。
- 将真实/隐藏数据作为测试数据。客户数据或秘密不应进入测试和存储;生成合成数据。
综上所述
人工智能减轻了编写测试的大部分重复负担:它生成快速的框架、大量的边缘情况列表以及覆盖差距分析。但最关键的一点是期望:人工智能倾向于测试代码的当前行为,而测试应该根据规范来编写。给出规则,检查期望,执行边缘情况,并测试测试是否确实通过注入错误来提供保护。测试覆盖率是一种工具,而不是目标。
应用任务
选择一个函数并首先通过简单地给出其代码来向 AI 打印一个测试;注意期望。然后再次打印测试,给出相同功能的规范(所需行为)。比较两个测试集的期望:是否有任何不同,哪一个揭示了真正的错误?最后,通过向代码添加有意的错误并查看测试中断来验证生成的测试之一是否有效。
清单
- [ ] 我区分测试是为了修复还是验证行为。
- [ ] 当我请求测试时,我给出应该到位的规则(规范),而不是代码。
- [ ] 我将每个生成的断言与规范进行比较。
- [ ] 我明确要求边缘和失败案例。
- [ ] 我将百分比覆盖率视为一种工具,而不是目标。
- [ ] 我测试测试是否确实通过注入错误来提供保护。