AI帮我写单元测试,Mockito的坑一个没少踩
起因:3000行Service类,0个单元测试
接手这个项目的时候我愣了一下——一个处理订单状态流转的OrderStateService,3000多行,没有一行测试代码。问了一圈才知道,之前的同事都嫌写测试太费劲,Mock依赖服务搞得人头大。CI/CD流水线全靠运气过。
我心想AI不是最擅长生成重复代码吗,单元测试这种套路化的东西,让它代劳是不是刚好?于是开始了三个月的"AI辅助写测试"实验。
结果算不上翻车,但也绝不是"AI一键生成完美测试"那种童话。
Mockito的argThat:AI生成的断言永远不对
第一个坑出在参数匹配上。OrderStateService有个方法会调用外部库存服务检查库存,AI给我生成的测试长这样:
@Test
public void shouldCancelOrderWhenStockInsufficient() {
InventoryResponse mockResp = new InventoryResponse();
mockResp.setAvailable(0);
when(inventoryService.checkStock(any(StockCheckRequest.class)))
.thenReturn(mockResp);
OrderResult result = orderStateService.processOrder(testOrder);
assertEquals(OrderStatus.CANCELLED, result.getStatus());
verify(inventoryService).checkStock(any(StockCheckRequest.class));
}
跑起来居然绿了。但我总觉得不对劲,这个测试太宽松了——any(StockCheckRequest.class)匹配任何参数,根本验证不了我们传给库存服务的参数是否正确。万一有人把skuId和warehouseId搞反了,测试照样通过。
我就让AI改成用argThat做精确匹配:
verify(inventoryService).checkStock(argThat(req ->
"SKU-8848".equals(req.getSkuId()) && "WH-SH-01".equals(req.getWarehouseId())
));
结果AI生成的这行代码直接编译不过。原因是argThat返回的是null需要显式类型转换,而AI完全不理解这个坑——它在训练数据里见过argThat的用法,但不了解Java泛型在Mockito里的类型推断限制。
修复后的版本:
verify(inventoryService).checkStock(
ArgumentMatchers.<StockCheckRequest>argThat(req ->
"SKU-8848".equals(req.getSkuId()) && "WH-SH-01".equals(req.getWarehouseId())
)
);
这个坑翻完我的感受是:AI对Mockito基础用法掌握得不错,一碰到泛型相关的边界case就容易露馅。你自己得懂这些细节才能发现AI写的问题,不然就是"测试全绿但什么都没测到"。
@SpyBean和@MockBean混用的隐蔽bug
项目里有个PaymentService内部调了自己的另一个方法,AI建议用@SpyBean做部分mock:
@SpyBean
private PaymentService paymentService;
@Test
public void testRefundFlow() {
doReturn(true).when(paymentService).validateRefund(any(RefundRequest.class));
// ...
}
这个用法的隐患是:@SpyBean会创建真实bean的代理,Spring上下文里的其他bean注入到PaymentService的引用可能跟spy对象不是同一个实例。结果verify阶段拿到的是原始bean而不是spy,verify一直失败,但又不报错——因为Mockito的宽松模式默认不抛异常。
这问题排查了两个多小时。AI给的方案思路是对的(用SpyBean处理内部方法调用),但Spring Test框架里SpyBean的代理机制它完全不理解。
我的经验是:涉及Spring容器和测试框架结合的复杂场景,别让AI直接给方案,自己先把架构理清,再让AI帮你生成具体的mock代码片段。
AI真正帮上忙的地方
说完了坑,讲讲AI在写测试上确实帮到了我的地方。
基础脚手架代码。一个类的测试框架——@ExtendWith、@MockBean声明、setUp方法里的通用mock setup——AI几秒钟就能搞定。这部分工作纯粹是体力活,以前花半小时写,现在半分钟。
@ExtendWith(MockitoExtension.class)
class OrderStateServiceTest {
@Mock
private InventoryService inventoryService;
@Mock
private PaymentService paymentService;
@Mock
private NotificationService notificationService;
@InjectMocks
private OrderStateService orderStateService;
private OrderDO testOrder;
@BeforeEach
void setUp() {
testOrder = OrderDO.builder()
.orderId("ORD-20260610-001")
.skuId("SKU-8848")
.quantity(2)
.build();
}
// AI生成到这就停了,具体测试逻辑自己写
}
边界条件补全。我把一个写好的正常路径测试丢给AI,让它列出"还有哪些边界条件没覆盖"。它能在几秒内给出一个清单:空值、负数quantity、并发重复提交、超时未支付、部分发货——这些我自己想也能想到,但AI不漏,这就比人强。
测试数据构造。复杂嵌套对象的Builder链式调用,AI写得很漂亮。以前构造一个带5层嵌套的DTO要写几十行setter,现在描述一下结构AI就给你生成好了。
三个月下来我的做法
现在我的工作流是这样:先自己设计测试策略——哪些路径必须覆盖、哪些mock行为是关键验证点。然后让AI生成脚手架和测试数据,自己写核心的断言和verify逻辑。最后把测试丢给AI做review,让它补充边界条件。
用AI写测试,核心逻辑必须自己把控。mock行为的正确性、断言的严谨程度、测试的独立性——这些才是决定测试质量的东西。AI能做的是加速你的执行,但设计思路和审查责任是程序员的。
别做那种"AI生成完直接commit"的事,我见过同事这么干,code review的时候发现测试里所有的mock都是any(),所有断言都是assertNotNull——这种测试除了拉低覆盖率什么用都没有。
以上就是我在实际项目里用AI辅助写Java单元测试的真实经历。这不是一篇"AI改变测试方式"的宣传文,而是记录了什么地方AI好用、什么地方必须靠人脑的实际体验。如果你也在用AI写测试,希望这些踩坑经历能帮你少走点弯路。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)