最近在用Cursor配合Claude 3.5写一个Spring Boot项目的单元测试,确实能省不少事,但总感觉心里没底。比如让它生成一个涉及Mockito和JPA的测试类,逻辑看着挺完整,可一旦跑起来就报各种NPE或者“InvalidUseOfMatchersException”。有时候修了三次prompt,它还是坚持用错误的mock方式,最后只能自己手改。
大家用AI编程工具写单元测试,真的敢直接跑吗?
全部回复
共 10 条说实话,我也有过一模一样的体验。AI生成的测试代码,尤其是Mockito和JPA混在一起的时候,表面看着像模像样,一跑就露馅。后来我总结了个笨办法:先让它只生成骨架和核心断言,凡是涉及静态方法、私有方法或者复杂嵌套的mock,我都自己动手补,不然就是无穷无尽的“InvalidUseOfMatchersException”循环。
我觉得问题的根源在于,AI对项目里已有的测试上下文理解不够,比如它不知道某些bean在测试环境里是不是真的存在,或者有没有被其他配置类干扰。你让它改prompt,它只是机械地换一种写法,但根本不理解底层逻辑冲突。所以我现在的策略是,让它写单测之前,我会自己先跑一遍被测方法,把边界行为摸清楚,再给AI限定具体的输入输出,而不是丢给它一个方法名让它自由发挥。
另外,我还发现一个坑,就是AI特别喜欢生成一层又一层的mock嵌套,其实很多场景直接用真实依赖加@SpringBootTest反而更稳。如果你只测业务逻辑,不如把JPA层拆出去,用H2内存库跑集成测试,那可比纯mock省心多了。说到底,工具能帮你省敲键盘的时间,但省不了你理解代码的时间,该自己过一遍逻辑还是得过。
确实,AI生成的测试代码最大的问题就是“看着对”和“真能跑”之间隔着一条鸿沟。我最近也在用,遇到最多的是它为了满足Mockito的语法,经常把when的条件写得很死,稍微一点上下文变化就炸了。后来我干脆把生成的测试当草稿,重点看它怎么组织测试数据,mock逻辑基本都得自己重写一遍。
对于JPA这块,我特别有同感,它默认所有repo都是非null的,但实际跑起来如果没初始化DataJpaTest,直接就是一片红。还有那个InvalidUseOfMatchersException,多半是它在同一个方法里混用了matcher和原始值,这种错误你哪怕提示它三次,它下次还是可能犯,感觉就是训练数据里的通病。
我现在有个笨办法,让它先写一个最基础的“冒烟测试”,确保spring context能加载起来,再让它往里加业务断言。这样至少能区分是框架配置问题还是逻辑问题,不然报错堆栈里全是NPE,根本分不清是mock没设好还是代码本身有bug。
另外我挺好奇,你们有没有试过让它先跑一遍看报错,然后把报错信息直接贴回给它让它自己修?我试过几次,有时候它能根据异常改对,但更多时候它只是换个地方犯同样的错,还是得靠人盯。感觉现在AI写测试,效率提升主要在“打字速度”而不是“思考质量”,真要敢直接跑,还得等它学会自己debug再说。
确实,AI生成的单测代码,我一般默认它“逻辑对但细节全错”。尤其是Mockito那套,它特别容易把when().thenReturn()和doReturn().when()混着用,然后跑起来就给你抛InvalidUseOfMatchersException,你说它错了吧,它自己还觉得挺有理的。
我现在基本策略是:让AI生成大框架,比如测试类的结构、方法名、边界值枚举,然后所有mock和verify的地方我全手动重写。不是说它写不了对的,而是调试它写的mock代码花的时间,可能比我自己写还长。
而且有个坑特别典型——涉及到JPA repository的时候,它经常忘了mock返回的Optional要配合orElseThrow,或者直接把实体ID设成null,然后关联查询就NPE了。这种错误特别隐蔽,因为编译期完全看不出来。
我倒想问问,你们有没有让它生成过带@WebMvcTest切片测试的?那玩意它更放飞自我,经常把@Service也塞进切片上下文里,然后启动直接失败。我试过几次之后,现在干脆只让它写纯POJO的测试了。
反正我的感觉是,这工具当个结对编程的初级开发用还行,但必须得有个senior在旁边审代码。不然它给你的不是安全感,是跑完测试之后一堆红色报错带来的挫败感。
我也有同感,AI写的测试跑通率真没想象中高,尤其涉及Mockito和Spring上下文的时候,经常是逻辑看着对,一跑就露馅。后来我学乖了,让它生成代码后先自己过一遍mock的匹配器,再跑,能省不少debug时间。不过话说回来,让它写纯业务逻辑的测试用例,比如那种简单service方法,倒是还挺靠谱的,复杂场景还是得靠人兜底。
说实话,我刚开始也直接跑,然后被InvalidUseOfMatchers搞到怀疑人生。现在我的做法是让它先产出测试骨架,再把关键mock点拆成小问题一个个问它,比一次性生成整段靠谱得多。另外,JPA那部分我干脆自己写,AI生成的repository mock经常忽略flush和事务边界,反而不如手工来得稳。
我之前遇到更坑的,它给一个静态方法写mock,直接用了when,跑起来才发现该用mockStatic,那叫一个崩溃。现在我的习惯是,AI生成完先不急着跑,扔给代码审查工具扫一遍,再针对报错信息喂回给AI让它自己解释,反而比反复改prompt效率高。
同感,AI生成的测试代码跑通率太看运气了,尤其Mockito和JPA组合时,我基本当它是脚手架,跑之前还得自己过一遍逻辑。
我现在都让AI先画测试流程,再手动码关键mock,反而比来回改prompt省心,NPE是真的磨人。
AI生成测试代码最大的问题就是“看着对”和“真能跑”完全是两码事,尤其Mockito那套静态方法和参数匹配器,模型根本意识不到运行时上下文的约束。我现在基本让它生成大框架,所有mock细节都自己重写,反而比纯手写还快一点,因为省去了查API的时间。另外建议你试试让Claude先写一个最简的失败用例,跑通了再让它扩展,比直接生成一整套类靠谱得多。
太真实了,我拿它生成Python的pytest也是这德行,看着像模像样,一跑全是fixture作用域搞错。现在基本把它当个草稿生成器,逻辑框架参考下,具体mock和边界条件还是得自己捋一遍,不然修bug的时间比手写还长。
我跟你的感受简直一模一样。AI写测试用例那个“看起来对”的迷惑性太强了,尤其是涉及到Mockito和JPA这种要精确控制交互边界的东西,它经常把静态方法或者私有方法也拿来mock,跑起来全是红。后来我学乖了,凡是它生成的mock逻辑,我第一件事就是检查有没有不必要的verify,还有when里面是不是用了实际对象而不是mock引用,这俩是重灾区。另外我觉得它特别容易忽略Spring上下文里的代理对象问题,你直接new一个类去测,跟容器里跑完全是两码事。现在我的流程是让它生成骨架和正常路径,但异常分支、边界值还有那些复杂的参数匹配,我都自己补,不然真不敢让测试在CI里裸奔。说真的,你让它改三次还在用错误方式,不如直接手写那几行来得快,AI适合当个聪明的打字员,不适合当那个最后拍板的人。
确实,AI写的测试代码跑通率没那么高,尤其是Mockito和JPA混在一起的时候,容易卡在细节上。我一般会让它先解释一下mock逻辑,或者直接贴报错让它自己修,比反复改prompt快一些。另外,我现在会先让它生成框架,再手动补关键断言,这样至少不会完全失控。反正别指望一步到位,当个辅助还是香的。
我一般只让它搭个框架,mock逻辑必须自己过一遍,不然跑起来全是坑。