我们期望单元测试能验证被修改逻辑的正确性,并帮助发现应用行为中的意外变化。在使用 AI 开发时,还必须检查任务范围之外的功能是否保持完好。
因此,验证新功能能够运行只是工作的一部分。我们还需要检查这次变更没有破坏应用中已有的规则。
单元测试应该保护什么
单元测试验证代码中各个部分的行为。它们同时也是防止回归的保障,即防止因改变原本正常运行的行为而引入的 bug。
这种保护对 AI 生成的代码尤其有价值。AI 可能满足了某个具体需求,却同时改动了本应保持不变的逻辑。
设计良好的测试有助于界定变更的边界:
这是我们想要改变的行为。这些规则必须继续成立。
根据风险和实际依赖来测试
我们不需要自动地为每个 getter、setter 或简单的数据类都编写单独的测试。
仅仅重复实现的测试可能会增加测试数量,却几乎没有增加价值。它还会在后续变更时带来更多维护工作。
一条实用的规则是:
针对具体的变更,重点关注被修改的行为以及依赖它的应用部分。根据潜在影响来确定测试范围。
对于没有判断逻辑的简单方法,如果其他测试已经充分覆盖了它们的行为,单独的单元测试可能就没有必要。
不过,仅凭简单并不能成为认为 AI 不会出错的理由。即使是一行的方法,也可能决定订单的价格或用户的访问权限。
在添加测试之前,先问三个问题:
- 这个任务中改变了什么行为?
我们验证变更满足需求。
- 哪些规则和依赖组件必须保持完好?
我们寻找变更可能导致回归的地方。
- 这个测试能发现真正的 bug 吗?
我们验证预期的行为,而不是简单地照抄实现。
测试必须与业务逻辑相连
最有价值的单元测试保护的是具体的应用规则。
例如,对于折扣计算方法,仅检查它返回一个数字是不够的。我们需要知道以下规则是否依然成立:
折扣不超过允许的上限;
最终价格不会低于零;
不符合折扣资格的客户支付全价;
计算遵循既定的舍入规则。
方法与业务规则之间的联系,才赋予测试意义。
实现可以改变。任务预期范围之外的规则必须保持完好。
AITM 如何处理这个问题
AITM 把 AI 开发与项目上下文、契约以及应用各部分之间的依赖关系连接起来。
在实现之前,architect 会确定应该改变什么、相关部分如何关联,以及哪些规则必须保持完好。随后 testing 步骤会使用这些信息。
因此,关键问题不只是 AI 能否编写测试,而是 AI 是否知道测试应该保护什么、为什么要保护。
设想对 DiscountService.calculateDiscount() 做一次修改。如果契约使用 dependsOnModules 记录了 OrderSummary.render() 也依赖于折扣计算,那么 testing 步骤就有理由同时检查对订单摘要的影响。
计算本身可以用单元测试来检查。而要检查该计算与订单摘要如何协同工作,集成测试可能更合适。
因此,测试是围绕具体变更的实际依赖和风险来设计的。
一个任务十个测试,另一个任务两千个
并非每个任务都需要相同范围的测试。
作为说明,在一个拥有 32,467 个测试的项目中,一次小改动可能只需要运行十个相关测试。而对共享业务逻辑的改动可能需要运行两千个。
有针对性测试的目的,是选出覆盖被修改行为及其依赖的测试。这可以缩短反馈周期,并使验证聚焦于任务的风险。
对于大范围的变更或新版本发布之前,更广泛的回归检查或完整的测试套件仍然有其用武之地。
运行的测试数量应当与变更的影响相匹配。
测试失败时,我们需要理解原因
失败的测试并不总是意味着同一种问题。我们需要区分以下几种情况:
实现中引入了 bug;
变更无意中违反了既有的业务规则;
新功能有意改变了预期行为,测试需要更新。
任务需求、契约和业务逻辑描述为这一判断提供了上下文。
如果需求确实发生了变化,相应的测试也必须随之改变。如果 AI 违反了本应保持完好的规则,则需要修复实现。
仅仅为了让测试重新通过而修改测试,可能会掩盖真正的 bug。
单元测试是第一层保护
每一级测试都有不同的作用:
单元测试验证逻辑的各个部分。
集成测试验证模块和系统如何协同工作。
E2E 测试在运行中的应用里检查完整的用户流程。
这些层次相互补充。每一层发现不同类型的 bug,并帮助从不同的角度验证变更。
因此,在使用 AI 开发时,我们要检查两件事:任务是否完成,以及预期范围之外的行为是否保持完好。
把任务需求、业务逻辑、依赖和测试连接起来,是 AITM 编排的重要组成部分。

例如:单元测试
了解更多:https://www.aitm.cz
#AITM #UnitTesting #SoftwareTesting #AI #SoftwareDevelopment