现在,越来越多的开发开始使用 AI 辅助编码。
完成功能代码后,我们还可以继续让 AI 生成单元测试、执行测试,并根据测试结果分析和修复问题。
不过在实际项目中,单元测试的覆盖情况并不总是理想:
- 有些团队缺少明确的单元测试规范;
- 有些开发还没有形成编写单元测试的习惯;
- 有些项目虽然有单元测试,但没有及时维护;
- 还有一些代码,在没有经过单元级验证的情况下就直接进入了测试阶段。
单元测试的主要责任人仍然是开发,但测试人员也可以适当前移。我们可以借助 AI 理解代码逻辑、识别边界场景,并检查或补充单元测试,从而更早发现实现遗漏。
下面给大家简单介绍单元测试的基本概念,以及利用AI辅助单元测试的思路。
*
一、单元测试在测什么?
单元测试是对代码中最小可测试单元做独立验证,通常由开发或测试开发编写,属于白盒测试。抓住三点就够:
1. 对象要小:通常是一个函数、方法,也可以是一个类或组件。范围越小,失败时越好定位。
2. 验证输入输出:给定输入,检查实际结果是否符合预期;正常值、临界值、异常输入都要覆盖。
3. 隔离外部依赖:数据库、第三方接口、文件系统等,常用 Mock 模拟返回,只验证当前单元逻辑。
单元测试是对代码中最小可测试单元进行独立验证的一种测试方式,通常由开发或测试开发人员编写和执行,属于白盒测试。
一个简单的例子:计算订单运费
假设商城有以下运费规则:
- 订单金额达到 99 元,免运费;
- 订单金额不足 99 元,收取 10 元运费;
- 订单金额不能为负数。
业务代码如下:
def calculate_shipping_fee(order_amount): if order_amount < 0: raise ValueError("订单金额不能为负数")
if order_amount >= 99:
return 0
return 10
```
根据这段业务逻辑,可以设计以下单元测试场景:
- 订单金额为 100 元,预期运费为 0 元;
- 订单金额为 50 元,预期运费为 10 元;
- 订单金额刚好为 99 元,预期运费为 0 元;
- 订单金额为 98.99 元,预期运费为 10 元;
- 订单金额为负数,预期抛出异常。
例如:
def test_free_shipping(): assert calculate_shipping_fee(100) == 0
def test_shipping_fee_required():
assert calculate_shipping_fee(50) == 10
def test_free_shipping_boundary():
assert calculate_shipping_fee(99) == 0
def test_invalid_order_amount():
with pytest.raises(ValueError):
calculate_shipping_fee(-1)
```
这组测试验证了运费计算规则,同时覆盖了正常场景、临界值和异常输入。
整个测试范围只包含 calculate_shipping_fee 函数,不需要创建真实订单,也不需要连接数据库。只要输入订单金额,就能判断运费计算结果是否正确。
这就是单元测试“小范围、独立验证”的特点。
*
二、如何利用AI辅助单元测试?
为了简化操作,我把单元测试相关流程拆成了 3 个 Skill:
- 第一个 Skill 负责分析目标代码并生成单元测试;
- 第二个 Skill 负责选择范围并执行单元测试;
- 第三个 Skill 负责分析测试失败原因,并在确认后进行修复和验证。
接下来,我们通过实际操作看一下完整流程。
第 1 步:拉取项目代码
首先,需要拿到项目代码。
在公司项目中,可以先向开发申请相应的代码权限,再通过 Git 将项目拉取到本地。
第 2 步:安装 Skill
将 Skill 放到项目对应的 Skill 目录中。
第 3 步:生成单元测试
安装完成后,就可以让 AI 为指定方法或模块生成单元测试。
这里建议提前明确测试范围,不要一开始就要求 AI 为整个项目生成单元测试。
一次处理的代码太多,不仅会消耗更多 Token,也会因为上下文过多而影响生成质量。比较合适的方式是从一个方法、一个类或一个小模块开始。
输入示例:
执行后,AI 会先扫描对应代码。
如果已经知道要测试哪个文件,也可以直接引用该文件,减少不必要的扫描范围。
最后,AI 会生成对应的单元测试代码。
下面是一段 Java 项目的生成结果:
@Testvoid create_shouldFail_whenIdCardExists() {// 1. 构造请求入参对象TenantCreateDTO req = buildCreateDTO(null);// 2. Mock:模拟 tenantService.checkIdCardExists 返回 true,代表身份证已经存在when(tenantService.checkIdCardExists(req.getIdCard(), null)).thenReturn(true);// 3. 调用被测方法,执行 tenantController.createResult<IdResponse> result = tenantController.create(req);// 4. 断言:校验返回码和提示文案assertEquals(400, result.getCode());assertEquals("身份证号已存在", result.getMessage());// 5. verify 校验:tenantService.save 方法不能被执行verify(tenantService, never()).save(any());}
第 4 步:执行单元测试
单元测试生成完成后,默认会自动执行。
执行结果如下:
当然,我们也可以手动执行测试。
例如,在 Java 项目中,可以使用下面的命令执行指定测试文件:
mvn -Dtest=单元测试文件 test
但不同语言、不同框架使用的测试命令并不相同。每次都去查询和记忆这些命令,会增加额外的操作成本。
因此,我又设计了一个用于执行单元测试的 Skill。
启动执行 Skill:
AI 会根据当前项目使用的语言、构建工具和测试范围生成对应命令,并自动执行:
最终执行结果如下:
第 5 步:分析并修复单元测试问题(可选)
开发修改代码后,原有单元测试可能会执行失败。
为了处理这种情况,我还提供了一个单元测试问题修复 Skill。
例如,下面是一次单元测试执行失败的结果:
这时,可以启动问题修复 Skill:
Skill 会先分析测试失败的原因,并给出对应的修改建议,等待我们确认:
AI 会根据确认后的方案进行修复,重新执行单元测试,并输出最终验证结果:
*
三、总结
这套流程把单元测试相关操作拆成了三个阶段:
1. 使用生成 Skill 分析代码并编写单元测试;
2. 使用执行 Skill 选择范围并运行测试;
3. 测试失败时,使用修复 Skill 分析原因、确认方案并重新验证。
对于不熟悉单元测试框架的人来说,不需要先记住大量语法和命令,也可以借助 AI 从一个具体的方法开始,逐步完成单元测试的生成、执行和问题处理。
对于测试人员来说,这也是一种向研发阶段前移的方式:通过阅读代码和检查单元测试,更早识别边界场景和实现遗漏,减少问题进入后续测试阶段的概率。