ChatGPT生成单元测试实践(Golang)

news/2024/4/28 6:40:07/文章来源:https://blog.csdn.net/qq_42739587/article/details/129674548

前言

目前gpt本质上是续写,所以在待测函数定义清晰的情况下,单元测试可以适当依赖它进行生成。

收益是什么:

  1. 辅助生成测试用例&测试代码,降低单元测试编写的心智成本
  2. 辅助code review,帮助发现代码显式/潜在问题

本文测试环境:

  • gpt: gpt-3.5-turbo
  • go:go 1.17

本文实践场景:企业微信美图鉴赏机器人


生成单元测试的工作流如下:

  1. 选定你的待测函数
  2. 分析函数的依赖:结构体定义、repo依赖、repo interface抽象(用fx或者wire等依赖注入框架的话会方便很多)
  3. 组织prompt,准备施法
  4. 吟唱魔法,得到输出单元测试代码
  5. copy到ide里面缝缝补补,与预期出入太多的话,调整prompt重来/要求gpt按照某个标准重写(比如:请按照testCases的写法重写组织测试数据)
  6. 运行测试用例

话不多说,先上基础prompt:

写下列写下列代码中[待测函数名]函数的单测,要求覆盖全部逻辑。 
断言使用require包。 
测试用例以testCases的形式输出。

下面的部分,我按照工作流的繁杂程度,简单区分了下easy/normal/hard情况。


Part1 easy:单个函数,无复杂依赖

单个函数,并且外部依赖都是开源库,那么直接把代码贴上去就行。

这里我们用图片压缩的独立函数举例:
(prompt前摇咒语 + 代码片段)

写下列代码中CompressPicture函数的单测,要求覆盖全部逻辑。
断言使用require包。
测试用例以testCases的形式输出。// CompressPicture the picture by resizing it to the specified width.
func CompressPicture(picData []byte, width uint) ([]byte, error) {// Decode the input image.oldBuffer := bytes.NewBuffer(picData)inputImg, _, err := image.Decode(oldBuffer)if err != nil {return nil, err}// Resize the image to the specified width.outputImg := resize.Resize(width, 0, inputImg, resize.Lanczos3)// Encode the output image as PNG and write it to a buffer.newBuffer := new(bytes.Buffer)err = png.Encode(newBuffer, outputImg)if err != nil {return nil, err}// Return the compressed image as a byte slice.return newBuffer.Bytes(), nil
}

ChatGPT输出的代码块如下:

func TestCompressPicture(t *testing.T) {// Setup test dataimgData, err := os.ReadFile("test-image.png")if err != nil {t.Fatalf("failed to read test image file: %v", err)}testCases := []struct {name  stringwidth uint}{{"compress with a valid width", 500},{"compress with an invalid width", 0},}for _, tc := range testCases {t.Run(tc.name, func(t *testing.T) {// Invoke the function being tested.compressedData, err := CompressPicture(imgData, tc.width)// Check the result.if err != nil {t.Fatalf("unexpected error: %v", err)}if len(compressedData) == 0 {t.Fatalf("compressed data is empty")}create, err := os.Create("test-image-compressed.png")if err != nil {return}defer create.Close()_, err = create.Write(compressedData)})}
}

放到ide里,完美运行,说明简单函数的单元测试生成还是很强的~

pass


Part2 normal :里面有一些外部import

有外部定义引入的时候,最好把外部定义直接贴上(不涉及mock)。

这里举例的函数出现了外部依赖 entity.ArchiveWithData等定义,所以最好要加上外部定义在输入中:

写下列代码中ConvertArchiveWithDataToBotTextMsg函数的单测,要求覆盖全部逻辑。
断言使用require包。
测试用例以testCases的形式输出。// ConvertArchiveWithDataToBotTextMsg 将画作稿件信息转换为企微机器人文本消息
func ConvertArchiveWithDataToBotTextMsg(data *entity.ArchiveWithData) *entity.BotMsgReq {var MentionedList []stringproxyUrl := data.Info.Urls.OriginalrawPixivUrl := fmt.Sprintf("https://www.pixiv.net/artworks/%d", data.Info.Pid)txt := &entity.BotText{Content:       fmt.Sprintf("proxy图源:%s\npixiv图源:%s", proxyUrl, rawPixivUrl),MentionedList: MentionedList,}postText := &entity.BotMsgReq{MsgType: entity.BotMsgText,Text:    txt,}return postText
}一些导入的定义如下:
type BotText struct {Content       string   `json:"content"`MentionedList []string `json:"mentioned_list"`
}
type BotMsgReq struct {MsgType BotMsgType `json:"msgtype"`Text    *BotText   `json:"text,omitempty"`
}
const (BotMsgText  BotMsgType = "text"
)type ArchiveWithData struct {Info ArchiveData []byte
}

下面是生成的代码,经过少量修改后可以直接运行:
(因为少输入了一个结构体的定义,导致它猜测了一些字段)

func Test_ConvertArchiveWithDataToBotTextMsg(t *testing.T) {testCases := []struct {name     stringdata     *entity.ArchiveWithDataexpected *entity.BotMsgReq}{{name: "regular data",data: &entity.ArchiveWithData{Info: entity.Archive{Pid: 123456,Urls: entity.PicUrl{Original: "https://example.com/image.jpg",},},Data: []byte{},},expected: &entity.BotMsgReq{MsgType: entity.BotMsgText,Text: &entity.BotText{Content: "proxy图源:https://example.com/image.jpg\npixiv图源:https://www.pixiv.net/artworks/123456",},},},{name: "data with special characters",data: &entity.ArchiveWithData{Info: entity.Archive{Pid: 789012,Urls: entity.PicUrl{Original: "https://example.com/image(with)special&characters.jpg",},},Data: []byte{},},expected: &entity.BotMsgReq{MsgType: entity.BotMsgText,Text: &entity.BotText{Content: "proxy图源:https://example.com/image(with)special&characters.jpg\npixiv图源:https://www.pixiv.net/artworks/789012",},},},}for _, tc := range testCases {t.Run(tc.name, func(t *testing.T) {result := ConvertArchiveWithDataToBotTextMsg(tc.data)require.Equal(t, tc.expected, result)})}
}

小修小补之后顺利运行


Part3 hard:对外部repo进行mock(gomock举例)

外部依赖越多,prompt template的构建可能就越复杂
ps.实际上gomonkey它也可以写
外部依赖repo的部分,要求gpt使用gomock进行对应repo的mock即可。
外部定义最好进行剪枝。

输入:

写下列代码中getSeTuDescSlice函数的单测,要求覆盖全部逻辑。
断言使用require包。
测试用例以testCases的形式输出。
对于m.repoSeTu.GetArchiveInfoSlice的调用,使用gomock进行mock处理,假设已经有mock文件。func (m *CronTab) getSeTuDescSlice(ctx context.Context, seTuConfig config.SeTuConfig) (entity.QueryResult, error) {queryResSlice, err := m.repoSeTu.GetArchiveInfoSlice(ctx, &entity.Query{R18:   seTuConfig.R18,Num:   1,Tag:   seTuConfig.Tags,Size:  seTuConfig.PicSize,Proxy: seTuConfig.Proxy,})if err != nil {return entity.QueryResult{}, err}return queryResSlice, nil
}下面是外部依赖的定义:
type SeTuConfig struct {SetuApiUrl string   `yaml:"setu_api_url"`QWAuth     []string `yaml:"qw_auth"`CronStr    string   `yaml:"cron_str"`R18        int64    `yaml:"r_18"`Tags       []string `yaml:"tags"`PicSize    []string `yaml:"pic_size"`Proxy      string   `yaml:"proxy"`
}// QueryResult 请求结果
type QueryResult struct {Error        string    `json:"error"`ArchiveSlice []Archive `json:"data"`//picPaths     []string
}

输出:

假设已经有mock文件:// mock_repoSetu 是通过mockgen自动生成的,用于模拟repoSetu接口的mock文件func TestCronTab_getSeTuDescSlice(t *testing.T) {testCases := []struct{name stringseTuConfig config.SeTuConfiggetArchiveInfoSliceRes entity.QueryResultgetArchiveInfoSliceErr errorwantErr bool}{{name: "success",seTuConfig: config.SeTuConfig{R18: 0,Tags: []string{"tag1", "tag2"},PicSize: []string{"large"},Proxy: "",},getArchiveInfoSliceRes: entity.QueryResult{ArchiveSlice: []entity.Archive{{Urls: []string{"pic1.url", "pic2.url"},Title: "pic1",Artists: []string{"artist1"},},},},getArchiveInfoSliceErr: nil,wantErr: false,},{name: "error",seTuConfig: config.SeTuConfig{R18: 1,Tags: []string{"tag3"},PicSize: []string{"small", "medium"},Proxy: "",},getArchiveInfoSliceRes: entity.QueryResult{},getArchiveInfoSliceErr: errors.New("get archive info slice error"),wantErr: true,},}for _, tc := range testCases {t.Run(tc.name, func(t *testing.T) {ctx := context.Background()// 创建mock对象mockRepoSeTu := mock_repoSetu.NewMockRepoSeTu(ctrl)m := &CronTab{repoSeTu: mockRepoSeTu}// 设置mock对象返回值mockRepoSeTu.EXPECT().GetArchiveInfoSlice(ctx, &entity.Query{R18: tc.seTuConfig.R18,Num: 1,Tag: tc.seTuConfig.Tags,Size: tc.seTuConfig.PicSize,Proxy: tc.seTuConfig.Proxy,}).Return(tc.getArchiveInfoSliceRes, tc.getArchiveInfoSliceErr)res, err := m.getSeTuDescSlice(ctx, tc.seTuConfig)if tc.wantErr {require.Error(t, err)} else {require.NoError(t, err)require.Equal(t, tc.getArchiveInfoSliceRes, res)}})}
}测试用例解释:
测试用例"success"模拟了getSeTuDescSlice调用成功的情况,
设置查询条件R18=0,Tags="tag1", "tag2",PicSize="large",期望返回多条图片信息;
测试用例"error"模拟了getSeTuDescSlice调用失败的情况,
设置查询条件R18=1,Tags="tag3",PicSize="small", "medium",期望返回错误信息。

生成的代码太长,几点总结:

  1. gomock代码可以正常生成,但是外部repo定义最好清晰明了,可以直接把interface定义贴上去,会被认知到。
  2. 粘贴到ide中,发现主要问题是包名导入问题,简单修改即可

生成的代码有一些报错:
有些报错

修改引入的类型名后即可:
小修小补

修改后运行成功:
run!

如果需要生成后人工修改量更低的话,需要更精确的上下文信息(包名、函数定义、描述等)


一些痛点

  1. ChatGPT单次对话有长度限制,所以一次尽量只进行一个函数的单元测试生成。
  2. 需要外部 repo mock的待测函数,需要的上下文很多,并且引入的无关字段需要剪枝避免污染输入
  3. 一些复杂的私有包,看起来是比较不好支持的(尝试过在prompt中教会他,但是效果不好)
  4. 一些复杂逻辑和边界情况,ai可能口是心非(写的用例和表述不一致,比如经典的20%*20%=400%,但是他知道这是错的)
  5. 对于分层不是很清晰的项目,对函数/repo打桩可能很困难,这个时候要描述清楚就比较困难了,gomonkey可能比较适用

其他用法

  1. 写好测试方法,让gpt帮助扩充用例集
  2. code review,下面的代码就是gpt给出的代码建议
帮我优化下面的代码// picCompress Modify size to compress pictures.
func picCompress(picData []byte) (newPicData []byte, err error) {oldBuf := bytes.NewBuffer(picData)pic, _, err := image.Decode(oldBuf)if err != nil {return}newPic := resize.Resize(uint(pic.Bounds().Dx()/2), 0, pic, resize.Lanczos3)var newBuf bytes.Buffererr = png.Encode(&newBuf, newPic)if err != nil {return}newPicData, err = ioutil.ReadAll(&newBuf)if err != nil {return}return
}
您好!下面是一个优化版的代码,主要改进在于:
减少对象的创建,避免额外的内存分配和垃圾回收。
增加错误检查,避免可能的错误被忽略。
以下是优化版代码:
// picCompress Modify size to compress pictures.
func picCompress(picData []byte) ([]byte, error) {reader := bytes.NewReader(picData)pic, _, err := image.Decode(reader)if err != nil {return nil, err}newPic := resize.Resize(uint(pic.Bounds().Dx()/2), 0, pic, resize.Lanczos3)var writer bytes.Buffererr = png.Encode(&writer, newPic)if err != nil {return nil, err}return writer.Bytes(), nil
}尝试减少了对象的创建,使用了 bytes.Reader 代替了 bytes.NewBuffer 和 ioutil.ReadAll。
在处理图片过程中,使用 bytes.Buffer 代替了 newBuf 这个额外的对象。
同时也将函数的返回值简化,只返回处理后的 []byte 和可能的错误,避免了空值的返回。最后,增加错误检查,保证代码健壮性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_94.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC:ChatGPT(一个里程碑式的对话聊天机器人)的简介(意义/功能/核心技术等)、使用方法(七类任务)、案例应用(提问基础性/事实性/逻辑性/创造性/开放性的问题以及编程相关)之详细攻略

AIGC:ChatGPT(一个里程碑式的对话聊天机器人)的简介(意义/功能/核心技术等)、使用方法(七类任务)、案例应用(提问基础性/事实性/逻辑性/创造性/开放性的问题以及编程相关)之详细攻略 导读:回拨到2020年5月,OpenAI提出了更强大的GPT-3模型&…

中科大chatgpt学术优化环境配置及部署

中科大chatgpt学术优化,环境配置及部署 github项目地址:https://github.com/binary-husky/chatgpt_academi 1、下载项目 1)通过 git 命令,将项目克隆到本地,并进行项目文件夹 git clone https://github.com/binary-husky/chat…

不会写代码,也能部署一个独立ChatGPT?

本教程使用GPT-3模型接口模拟ChatGPT项目,虽然与真正的ChatGPT存在差异,但是演示了ChatGPT的工作原理。 (ChatGPT服务是基于GPT-3模型,经过大量的微调训练而来的,本教程暂时不包含训练内容,之后我们会讲如…

ChatGPT 的 10 种集成模式:从开源 AI 体验平台 ClickPrompt 中受到的启发

和国内外的很多公司一样,在 Open AI 公司开放了 ChatGPT API 接口之后,我们也在探索如何去结合到业务中。而在探索的过程中,我们发现了一个问题,大部分的业务人员并不了解 AI 的能力,所以我们开源构建了 ClickPrompt&a…

ChatGPT有多强?真的能替代程序员?一起来看看!

最近网络上很多文章都在说ChatGPT,神乎其神,我也试着玩了一下,这里分享下过程。 从结果来说,它离替代程序员还有很长的一段路要做。因为程序的工作虽然是由各个小模块组成,但是一个系统性的大逻辑,可能是现…

【分享】21个ChatGPT镜像站

分享21个能在国内无需科学上网使用ChatGPT的网站 ChatGPT镜像站(更新GPT4镜像站) AI EDU https://theb.ai/ https://poe.com/ https://dev.yqcloud.top/ https://chat.uue.me/ GeekChat https://qachat.vercel.app ChatGPT3.5国产镜像-博弈Ai Ch…

chatgpt免费获取KEY-chatgpt免费版生成文本

chatgpt有免费版本吗?OpenAI提供了两种版本的GPT模型,即GPT-2和GPT-3。其中GPT-2的某些层次在2019年对外开放,并提供了一些API,可以供用户免费试用,并在规定范围内广泛应用。但例如访问GPT API,调用更高等级…

chatGPT 中科院学术优化 超详细安装教程

1.下载 项目地址:https://github.com/binary-husky/chatgpt_academic 第一种:可以直接下载zip安装包,然后直接解压。 第二种:采用git来下载 git clone https://github.com/binary-husky/chatgpt_academic.git cd chatgpt_acad…

如何在新必应(New Bing)使用Chatgpt功能

1.效果展示 chatgpt回答的还是很全面的。 2.准备工作 2.1准备1个邮箱用来接收候选人名单通过的消息,我用的微软的outlook免费邮箱,可以直接点击这里注册 2.2下载最新的Microsoft Edge浏览器(),然后下载这个modheader…

开发自己私有chatGPT(六)训练自己的chatGPT openai模型,微调模型,列出微调任务,使用自定义模型教程...

在开发私有chatGPT的时候,如果我们使用官方的基础模型,那么回答的问题可能不符合我们自己的预设答案 现在就是通过官方的训练方法,提供一部分数据进去,训练出自己的私有模型 按照工具 pip install --upgrade openai设置环境变量 e…

ChatGPT提问指令大全(建议收藏)

在使用 ChatGPT 时,当你给的指令越精确,它的回答会越到位,举例来说,假如你要请它帮忙写文案,如果没给予指定情境与对象,它会不知道该如何回答的更加准确。 一、写报告 1、我现在正在 [报告的情境与目的]。…

Github的开源项目ChuanhuChatGPT和gpt-academic,专用于学术拓展ChatGPT

目录1 前言2 Chuanhu ChatGPT的安装部署3 ChatGPT学术优化3.1 主要功能3.2 下载软件安装运行3.3 用docker运行3.4 自定义快捷键(类似function)3.5 配置代理4 讨论1 前言 专用于科研工作的拓展Tool,优化Paper润色、markdown、Tex公式双显示、…

【ChatGPT】参加计算机科学考试(GPT-4对比GPT-3.5)

ChatGPT真的“无敌”了吗???? 当【ChatGPT】参加大学生计算机科学考试1.简介2.介绍3.ChatGPT(GPT-4对比GPT-3.5)参加计算机考试实验4.实验设计4.1实验介绍4.2 对ChatGPT提出问题4.3测试结果5. GTP-46. 讨论1.简介 我们…

ChatGPT调研

ChatGPT调研背景:ChatGPT简单介绍FINETUNED LANGUAGE MODELS ARE ZERO-SHOT LEARNERSFine-Tuning Language Models from Human PreferencesLearning to Summarize with Human FeedbackTraining language models to follow instructions with human feedbackChatGPTC…

ChatGPT在智能投顾领域应用探讨

ChatGPT上线不到一周用户突破100万,据报道称,ChatGPT开发者OpenAI预计ChatGPT明年年营收将会达到2亿美元,2024年营收将达到10亿美元。业内也都是ChatGPT的溢美之词,关于其实现的探讨已有不少。对其在具体场景中的应用探讨不多&…

如何使用自己的chatgpt

怎么开始chatgpt 对普通用户来说可以简单认为就是一个智能聊天机器人,比以往的智能机器人更加强大。要使用ChatGPT要做哪些准备工作呢 使用的工具 一个google邮箱账号 sms 接码平台 如何开始 首先点开 chatgpt官网 用google账号注册 然后就是短信验证 由于没有…

中科院学术专用版 ChatGPT,开源了!

公众号关注 “GitHubDaily”设为 “星标”,每天带你逛 GitHub!周末的时候,一位开发者将名为「ChatGPT Academic」的项目开源至 GitHub。仅用了短短一两天,该项目 Star 数便增长到了 1800,成为 GitHub 上又一个基于 Cha…

使用简单的编程为您的网站构建具有自定义知识的类似 ChatGPT 的聊天机器人

与 ChatGPT 类似,但其形式可以插入您的网站,并通过将基本的“老派”NLP 与尖端的 GPT-3 相结合,使用任何类型的定制信息进行扩展。 介绍 在 2022 年底,您很可能已经听说过 ChatGPT,甚至通过使用它见证了它的强大。ChatGPT 是一项革命性的人工智能技术,它允许用户与非常智…

快速集成ChatGPT,请收藏好这份入门指南(Python版)

ChatGPT 是 GPT-3 语言模型的变体,专为会话语言生成而设计。如果要在 Python 中使用 ChatGPT,您需要安装 OpenAI API 客户端并获取 API 密钥。在本文中,我们将设置一个简单的示例,教您在 Python 程序中使用 ChatGPT 所需的确切步骤…

ChatGPT和New Bing作为AI界新宠,两者有何异同

ChatGPT和New Bing是两个不同的实体,它们之间有一些区别也有一些相似之处。我先说说各自的特点,再汇总说说两者的异同点。 ChatGPT的特点: ChatGPT是一个基于神经网络的自然语言处理模型,能够自动生成自然语言响应。ChatGPT的模型…