前言
目前gpt本质上是续写,所以在待测函数函数定义清晰的情况下,单元测试可以适当依赖它进行生成。
收益是什么:
辅助生成测试用例&测试代码,降低单元测试编写的心智成本
辅助code review,帮助发现代码显式/潜在问题
本文测试环境:
gpt: gpt-3.5-turbo
go:go 1.17
本文实践场景:企业微信美图鉴赏机器人
生成单元测试的工作流如下:
选定你的待测函数
分析函数的依赖:结构体定义、repo依赖、repo interface抽象(用fx和wire等依赖注入框架的话会方便很多)
组织prompt,准备施法
吟唱魔法,得到输出单元测试代码
copy到ide里面缝缝补补,与预期出入太多的话,调整prompt重来/要求gpt按照某个标准重写(比如:请按照testCases的写法重写组织测试数据)
运行测试用例
话不多说,先上基础prompt:
下面的部分,我按照工作流的繁杂程度,简单区分了下easy/normal/hard情况。
单个函数,并且外部依赖都是开源库,那么直接把代码贴上去就行。
这里我们用图片压缩的独立函数举例:(prompt前摇咒语 + 代码片段)
ChatGPT输出的代码块如下:
放到ide里,完美运行,说明简单函数的单元测试生成还是很强的~
Part2 normal :里面有一些外部import
有外部定义引入的时候,最好把外部定义直接贴上(不涉及mock)。
这里举例的函数出现了外部依赖 entity.ArchiveWithData等定义,所以最好要加上外部定义在输入中:
下面是生成的代码,经过少量修改后可以直接运行:
(因为少输入了一个结构体的定义,导致它猜测了一些字段)
Part3 hard:对外部repo进行mock(gomock举例)
外部依赖越多,prompt template的构建可能就越复杂
ps.实际上gomonkey它也可以写
外部依赖repo的部分,要求gpt使用gomock进行对应repo的mock即可。
外部定义最好进行剪枝。
输出:
生成的代码太长,几点总结:
1.gomock代码可以正常生成,但是外部repo定义最好清晰明了,可以直接把interface定义贴上去,会被认知到。
2.粘贴到ide中,发现主要问题是包名导入问题,简单修改即可
修改后运行成功:
如果需要生成后人工修改量更低的话,需要更精确的上下文信息(包名、函数定义、描述等)
一些痛点
1.需要外部 repo mock的待测函数,需要的上下文很多,并且引入的无关字段需要剪枝避免污染输入
2.一些复杂的私有包,看起来是比较不好支持的(尝试过在prompt中教会他,但是效果不好)
3.一些复杂逻辑和边界情况,ai可能口是心非(写的用例和表述不一致,比如经典的20%*20%=400%,但是他知道这是错的)
4.对于分层不是很清晰的项目,对函数/repo打桩可能很困难,这个时候要描述清楚就比较困难了,gomonkey可能比较适用
其他用法
1.写好测试方法,让gpt帮助扩充用例集
2.code review,下面的代码就是gpt给出的代码建议
ChatGPT给出的优化建议: