用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

这篇具有很好参考价值的文章主要介绍了用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 1. 好险,差点被喷
  • 2. 重构测试代码
    • 2.1 引入 Ginkgo 测试框架
    • 2.2 尝试改造旧的测试用例
    • 2.3 重构功能测试代码
  • 3. 总结

1. 好险,差点被喷

早几天发了一篇文章:《仅三天,我用 GPT-4 生成了性能全网第一的 Golang Worker Pool,轻松打败 GitHub 万星项目》,这标题是挺容易被怼,哇咔咔;不过最终“下场”不算惨,怼的声音很少,相反很多开发者马上就在自己的项目里开始使用 GoPool了,刺激!

等等,你以为“刺激”的只是“我”?咳咳,其实,那些开发者也感觉很“刺激”。这不,他们被螃蟹壳塞牙了,你看 GoPool 的 issue 列表:

用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

容我辩解5秒钟)Bugs 有归有,不过平均一个 issue 的解决时间没有超过半天,至少我提供的售后服务还可以的,对吧?哈哈……

5秒到了)不得不承认:一个足够健壮的 Worker Pool 还是需要花点时间来打磨。

那就再打磨一下吧:

  • 《让 GPT-4 来修复 Golang “数据竞争”问题》
  • 《让 GPT-4 来修复 Golang “数据竞争”问题(续)》

2. 重构测试代码

当前所有测试用例都放在 gopool_test.go 中,这个文件已经接近200行了,维护起来开始吃力。所以我决定今天先让 GPT-4 来改造一下测试用例的组织方式。

下文使用 DevChat 和 GPT-4 交互。如果你对 DevChat 感兴趣,可以跳转阅读这篇文章:

  • 《DevChat:将 GPT-4 无缝融入 VS Code,极致提升你的编程体验》
  • 如果你想要注册 DevChat,点击这个链接可以免费注册,目前 DevChat 还在公测中,不收费。

2.1 引入 Ginkgo 测试框架

  • 我对 DevChat 说:

当前我的测试代码全部在 gopool_test.go 中,我觉得可读性不够好。我希望你引入 Ginkgo 测试框架来帮我重新组织测试代码。首先我只需要你告诉我如何引入 Ginkgo,搭建测试骨架代码,而不用编写具体的测试用例。

DevChat 的回答如下:


要引入 Ginkgo 测试框架并搭建测试骨架代码,你需要按照以下步骤进行:

  1. 首先,你需要安装 Ginkgo 和 Gomega。Ginkgo 是一个 Go 测试框架,Gomega 是一个配套的断言库。你可以通过以下命令安装它们:
go get github.com/onsi/ginkgo/ginkgo
go get github.com/onsi/gomega/...
  1. 然后,你需要在你的测试目录下生成一个新的 Ginkgo 测试套件。你可以通过以下命令生成:
ginkgo bootstrap

这将会生成一个名为 suite_test.go 的文件,其中包含了一个基本的测试套件骨架。

  1. 接下来,你可以为你的 gopool 包生成一个 Ginkgo 测试文件。你可以通过以下命令生成:
ginkgo generate gopool

这将会生成一个名为 gopool_test.go 的文件,其中包含了一个基本的测试文件骨架。

  1. 最后,你可以在 gopool_test.go 文件中添加你的测试用例。以下是一个基本的测试用例骨架:
var _ = Describe("Gopool", func() {
    Context("when something happens", func() {
        It("should do something", func() {
            // Your test code goes here
        })
    })
})

在这个骨架中,Describe 用于描述你正在测试的对象,Context 用于描述你正在测试的场景,It 用于描述你的期望结果。

以上就是引入 Ginkgo 并搭建测试骨架代码的步骤。你可以根据你的需求修改和添加测试用例。


因为 gopool_test.go 文件已经存在,Ginkgo 也没有智能到能够直接“merge”,所以我先将当前代码备份到了 gopool_1_test.go 中:

用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

2.2 尝试改造旧的测试用例

骨架有了,接下来我准备尝试让 GPT-4 来重写代码。

  • 我对 DevChat 说:

我将原先的测试用例到备份到了 gopool_1_test.go 中,现在我需要你用 Ginkgo 的最佳实践重构测试代码,新的代码将会放到 gopool_test.go 中。

DevChat 的回答如下:


首先,我们需要将原先的测试用例转换为 Ginkgo 的格式。这包括将每个测试用例转换为一个 It 块,并使用 Gomega 断言库来替换原先的断言。以下是转换后的测试代码:

package gopool_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/devchat-ai/gopool"
)

var _ = Describe("Gopool", func() {
	Describe("With Mutex", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})

	Describe("With SpinLock", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})

	Describe("With Error", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})

	Describe("With Result", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})

	Describe("With Retry", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})

	Describe("With Timeout", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})

	Describe("With MinWorkers", func() {
		It("should work correctly", func() {
			// Your test code goes here
		})
	})
})

这只是一个骨架代码,你需要将原先的测试代码复制到对应的 It 块中,并使用 Gomega 断言库来替换原先的断言。例如,你可以将 t.Errorf 替换为 Expect().To(Equal())

对于基准测试,Ginkgo 并不直接支持,但你可以使用 Go 的原生 testing 包来进行基准测试。你可以将基准测试代码放在一个单独的文件中,例如 gopool_benchmark_test.go


啥?我感觉 GPT-4 偷懒了,居然不直接给我把代码写好。另外我没记错的话,Ginkgo 是支持“基准测试”的,可能是 GPT-4 的训练数据太旧导致它不知道这件事。(没错,这时候是它不是她。)

用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

于是我先“笑纳”了这段代码,然后拆解问题,暂且将压测代码放到单独的文件,让 GPT-4 安心把功能测试代码重构好,然后我再和它算“基准测试”的账。

此刻我就有了一个新文件:

  • gopool_benchmark_test.go

里面内容如下:

package gopool

import (
	"sync"
	"testing"
	"time"

	"github.com/daniel-hutao/spinlock"
)



func BenchmarkGoPoolWithMutex(b *testing.B) {
	var wg sync.WaitGroup
	var taskNum = int(1e6)
	pool := NewGoPool(1e4, WithLock(new(sync.Mutex)))
	defer pool.Release()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		wg.Add(taskNum)
		for num := 0; num < taskNum; num++ {
			pool.AddTask(func() (interface{}, error) {
				time.Sleep(10 * time.Millisecond)
				wg.Done()
				return nil, nil
			})
		}
		wg.Wait()
	}
	b.StopTimer()
}

func BenchmarkGoPoolWithSpinLock(b *testing.B) {
	var wg sync.WaitGroup
	var taskNum = int(1e6)
	pool := NewGoPool(1e4, WithLock(new(spinlock.SpinLock)))
	defer pool.Release()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		wg.Add(taskNum)
		for num := 0; num < taskNum; num++ {
			pool.AddTask(func() (interface{}, error) {
				time.Sleep(10 * time.Millisecond)
				wg.Done()
				return nil, nil
			})
		}
		wg.Wait()
	}
	b.StopTimer()
}

func BenchmarkGoroutines(b *testing.B) {
	var wg sync.WaitGroup
	var taskNum = int(1e6)

	for i := 0; i < b.N; i++ {
		wg.Add(taskNum)
		for num := 0; num < taskNum; num++ {
			go func() (interface{}, error) {
				time.Sleep(10 * time.Millisecond)
				wg.Done()
				return nil, nil
			}()
		}
		wg.Wait()
	}
}

gopool_1_test.go 的内容我就不赘述了,就是原先的全量测试代码减去上面这些基准测试代码。那么接下来问题就拆分成了2个:

  1. 用 Ginkgo 的方式改造功能测试代码
  2. 用 Ginkgo 的方式改造基准测试代码

不过目测本文篇幅有点长了,所以我决定今天先完成功能测试代码的改造,基准测试下次再说。

2.3 重构功能测试代码

接下来该让 GPT-4 开始重构功能测试代码了。

  • 我对 DevChat 说:

帮我将 gopool_1_test.go 中的用 testing 库编写的测试代码全部用 Ginkgo 的方式重构,更新到 gopool_test.go 中。

用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

提示: DevChat 给出的代码库右上角有一个“diff”按钮,点一下可以进入“diff 视图”,这样就能看到当前打开文件中的代码和 GPT-4 给出的代码之间的 diff,然后你就能清晰地看到 GPT-4 给出的代码具体修改了什么了。

DevChat 的完整回答是:


好的,我将会按照你的要求,将 gopool_1_test.go 中的测试代码用 Ginkgo 的方式重构,并更新到 gopool_test.go 中。

package gopool_test

import (
	"errors"
	"sync"
	"sync/atomic"
	"time"

	"github.com/daniel-hutao/spinlock"
	"github.com/devchat-ai/gopool"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Gopool", func() {
	Describe("With Mutex", func() {
		It("should work correctly", func() {
			pool := gopool.NewGoPool(100, gopool.WithLock(new(sync.Mutex)))
			defer pool.Release()
			for i := 0; i < 1000; i++ {
				pool.AddTask(func() (interface{}, error) {
					time.Sleep(10 * time.Millisecond)
					return nil, nil
				})
			}
			pool.Wait()
		})
	})

	Describe("With SpinLock", func() {
		It("should work correctly", func() {
			pool := gopool.NewGoPool(100, gopool.WithLock(new(spinlock.SpinLock)))
			defer pool.Release()
			for i := 0; i < 1000; i++ {
				pool.AddTask(func() (interface{}, error) {
					time.Sleep(10 * time.Millisecond)
					return nil, nil
				})
			}
			pool.Wait()
		})
	})

	Describe("With Error", func() {
		It("should work correctly", func() {
			var errTaskError = errors.New("task error")
			pool := gopool.NewGoPool(100, gopool.WithErrorCallback(func(err error) {
				Expect(err).To(Equal(errTaskError))
			}))
			defer pool.Release()

			for i := 0; i < 1000; i++ {
				pool.AddTask(func() (interface{}, error) {
					return nil, errTaskError
				})
			}
			pool.Wait()
		})
	})

	Describe("With Result", func() {
		It("should work correctly", func() {
			var expectedResult = "task result"
			pool := gopool.NewGoPool(100, gopool.WithResultCallback(func(result interface{}) {
				Expect(result).To(Equal(expectedResult))
			}))
			defer pool.Release()

			for i := 0; i < 1000; i++ {
				pool.AddTask(func() (interface{}, error) {
					return expectedResult, nil
				})
			}
			pool.Wait()
		})
	})

	Describe("With Retry", func() {
		It("should work correctly", func() {
			var retryCount = int32(3)
			var taskError = errors.New("task error")
			var taskRunCount int32 = 0

			pool := gopool.NewGoPool(100, gopool.WithRetryCount(int(retryCount)))
			defer pool.Release()

			pool.AddTask(func() (interface{}, error) {
				atomic.AddInt32(&taskRunCount, 1)
				if taskRunCount <= retryCount {
					return nil, taskError
				}
				return nil, nil
			})

			pool.Wait()

			Expect(atomic.LoadInt32(&taskRunCount)).To(Equal(retryCount + 1))
		})
	})

	Describe("With Timeout", func() {
		It("should work correctly", func() {
			var taskRun int32

			pool := gopool.NewGoPool(100, gopool.WithTimeout(100*time.Millisecond), gopool.WithErrorCallback(func(err error) {
				Expect(err.Error()).To(Equal("task timed out"))
				atomic.StoreInt32(&taskRun, 1)
			}))
			defer pool.Release()

			pool.AddTask(func() (interface{}, error) {
				time.Sleep(200 * time.Millisecond)
				return nil, nil
			})

			pool.Wait()

			Expect(atomic.LoadInt32(&taskRun)).To(Equal(int32(1)))
		})
	})

	Describe("With MinWorkers", func() {
		It("should work correctly", func() {
			var minWorkers = 50

			pool := gopool.NewGoPool(100, gopool.WithMinWorkers(minWorkers))
			defer pool.Release()

			Expect(pool.GetWorkerCount()).To(Equal(minWorkers))
		})
	})
})

这段代码将 gopool_1_test.go 中的测试用例转换为 Ginkgo 的格式,并使用 Gomega 断言库来替换原先的断言。


这段代码基本是 ok 的,不过运行的时候会遇到这个错误:

go test ./...
/var/folders/gv/f94q_vyx2m17d_36sf0zkm0r0000gn/T/go-build3608449998/b001/gopool.test flag redefined: ginkgo.seed
panic: /var/folders/gv/f94q_vyx2m17d_36sf0zkm0r0000gn/T/go-build3608449998/b001/gopool.test flag redefined: ginkgo.seed

goroutine 1 [running]:
flag.(*FlagSet).Var(0x14000114120, {0x1049bd108, 0x104c0d4a0}, {0x1400011af70, 0xb}, {0x10486d229, 0x2a})
        /usr/local/go/src/flag/flag.go:982 +0x2a4
flag.(*FlagSet).Int64Var(...)
        /usr/local/go/src/flag/flag.go:769
github.com/onsi/ginkgo/config.Flags(0x14000191ba8?, {0x10485657a?, 0x989680?}, 0x1)
        /Users/danielhu/go/pkg/mod/github.com/onsi/ginkgo@v1.16.5/config/config.go:75 +0xe0
github.com/onsi/ginkgo.init.0()
        /Users/danielhu/go/pkg/mod/github.com/onsi/ginkgo@v1.16.5/ginkgo_dsl.go:53 +0x38
FAIL    github.com/devchat-ai/gopool    3.889s
FAIL

这时候 Google 一下,可以找到 Ginkgo issue 875。改起来也很容易,在 gopool_test.go 的开头应该将 . "github.com/onsi/ginkgo" 改成 . "github.com/onsi/ginkgo/v2"

所以目前 go.mod 就变成了这样:

module github.com/devchat-ai/gopool

go 1.20

require (
	github.com/daniel-hutao/spinlock v0.1.0
	github.com/onsi/ginkgo/v2 v2.11.0
	github.com/onsi/gomega v1.27.10
)

require (
	github.com/go-logr/logr v1.2.4 // indirect
	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
	github.com/google/go-cmp v0.5.9 // indirect
	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
	golang.org/x/net v0.12.0 // indirect
	golang.org/x/sys v0.10.0 // indirect
	golang.org/x/text v0.11.0 // indirect
	golang.org/x/tools v0.9.3 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)

再装一个 VS Code 的 ginkgo 插件,圆满了:

用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

通过 ginkgo 插件运行一下:

用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)

也可以在命令行里自己敲 ginkgo -v 感受下更详细的结果输出:

$ ginkgo -v
Running Suite: Gopool Suite - /Users/danielhu/go/mycode/gopool
==============================================================
Random Seed: 1692081153

Will run 7 of 7 specs
------------------------------
Gopool With Mutex should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:17
• [0.203 seconds]
------------------------------
Gopool With SpinLock should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:31
• [0.201 seconds]
------------------------------
Gopool With Error should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:45
• [0.102 seconds]
------------------------------
Gopool With Result should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:62
• [0.102 seconds]
------------------------------
Gopool With Retry should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:79
• [0.101 seconds]
------------------------------
Gopool With Timeout should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:102
• [0.202 seconds]
------------------------------
Gopool With MinWorkers should work correctly
/Users/danielhu/go/mycode/gopool/gopool_test.go:123
• [0.001 seconds]
------------------------------

Ran 7 of 7 Specs in 0.916 seconds
SUCCESS! -- 7 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS

Ginkgo ran 1 suite in 4.759009833s
Test Suite Passed

到现在,测试相关的文件就有了3个:

  • gopool_benchmark_test.go
  • gopool_suite_test.go
  • gopool_test.go

到此,提个 PR :Refactor tests using Ginkgo and Gomega

3. 总结

因为 GPT-4 的训练数据是大约2年前的,也就是最近2年这个世界发生了啥它是不知道的。所以对于一些变化大的库,对于一些版本敏感的问题,你要对 GPT 给出的代码保持警惕。有时候你需要的代码对版本不敏感,那无所谓;反之,及时 Google 一下。

总之,擅用 GPT,但别只用 GPT。偶尔还是想想你的老相好“Google Bing 和度娘”。文章来源地址https://www.toymoban.com/news/detail-651005.html

到了这里,关于用 GPT-4 给开源项目 GoPool 重构测试代码 - 每天5分钟玩转 GPT 编程系列(8)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 研究开源gpt-2-simple项目,跑一个简单的模型,然后生成一段对话。用的是 Intel(R) Core(TM) i7-9700,8核8线程,训练最小的模型200次跑1个小时20分钟

    本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 未经博主允许不得转载。 博主CSDN地址是:https://blog.csdn.net/freewebsys 博主掘金地址是:https://juejin.cn/user/585379920479288 博主知乎地址是:https://www.zhihu.com/people/freewebsystem 快速使用docker 镜像进行环境搭建。 相关的

    2024年02月09日
    浏览(42)
  • GPT SOVITS项目 一分钟克隆 (文字输出)

    步骤流程:(首先使用UVR 提取人声文件,然后按下面步骤进行) 注意这里提交的音频是参考的音频

    2024年02月20日
    浏览(33)
  • 【Java设计模式 规范与重构】 二 重构的保障:单元测试,以及如何提高代码可测试性

    其实之前的工作中强调过很多次自己做测试的重要性,例如讲单元测试的: 【C#编程最佳实践 一】单元测试实践 ,讲单元测试规范的 【阿里巴巴Java编程规范学习 四】Java质量安全规约 ,讲接口测试的: 【C#编程最佳实践 十三】接口测试实践 ,这里旧事重提就不再详细展开

    2023年04月25日
    浏览(37)
  • 基于scrcpy的Android群控项目重构,集成Appium服务执行自动化测试用例

    基于scrcpy的Android群控项目重构 基于scrcpy的Android群控项目重构 进阶版 基于scrcpy的Android群控项目重构,获取Android屏幕元素信息并编写自动化事件(视频) 基于scrcpy的Android群控项目重构,获取Android屏幕元素信息并编写自动化事件(博客) 基于scrcpy的Android群控项目重构,集成

    2024年02月16日
    浏览(43)
  • 每天十分钟学会Spark

    Spark是什么 Spark是一种基于内存的快速、通用、可拓展的大数据分析计算引擎。 Spark官网:http://spark.apache.org/ Spark的特点 1、快速   一般情况下,对于迭代次数较多的应用程序,Spark程序在内存中的运行速度是Hadoop MapReduce运行速度的100多倍,在磁盘上的运行速度是Hadoop MapRedu

    2024年03月18日
    浏览(43)
  • 每天5分钟玩转Kubernetes | 汇总

    书籍来源:cloudman《每天5分钟玩转Kubernetes》 一边学习一边整理老师的课程内容及试验笔记,并与大家分享,侵权即删,谢谢支持! 1. 每天5分钟玩转Kubernetes | 先把Kubernetes跑起来_COCOgsta的博客-CSDN博客 2. 每天5分钟玩转Kubernetes | 重要概念_COCOgsta的博客-CSDN博客 3. 每天5分钟玩

    2024年02月14日
    浏览(25)
  • 《每天5分钟玩转kubernetes》读书笔记

    Pod是脆弱的,但应用是健壮的。 kubelet运行在Cluster所有节点上,负责启动Pod和容器。kubeadm用于初始化Cluster。kubectl是k8s命令行工具。通过kubectl可以部署和管理应用,查看各种资源,创建、删除和更新各种组件。 Pending、ContainerCreating、ImagePullBackOff都表明Pod没有就绪,Running才是

    2024年02月14日
    浏览(27)
  • 10分钟学会Springboot项目单元测试

    本篇来聊一下项目开发过程中至关重要的一个话题:单元测试。 早期在一些大公司的传统业务部门,比如:微软的windows、office等部门,研发和测试人员的比例基本是1:1到2:1之间,一个核心模块的测试用例动辄几十万个test case,而一次full-test都要跑几天、甚至几个星期。每个

    2024年02月06日
    浏览(47)
  • 每天五分钟计算机视觉:揭秘迁移学习

    随着人工智能的迅速发展,深度学习已经成为了许多领域的关键技术。然而,深度学习模型的训练需要大量的标注数据,这在很多情况下是不现实的。迁移学习作为一种有效的方法,可以在已有的数据和模型上进行训练,然后将其应用于新的任务。这种方法大大降低了对新任

    2024年01月24日
    浏览(41)
  • 9个已开源的GPT4平替分享(附开源代码+论文)

    资料整理自网络,有误欢迎指正 对于想要研究大模型的同学来说,目前ChatGPT无疑是最好的学习对象,但等它开源估计是不太可能了,所以学姐今天整理了一些开源的类GPT模型,帮助大家更好的理解大模型背后的机理。 PS:有的功能还不错,也可以上手玩耍玩耍。 开源链接都

    2024年02月09日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包