这是我参与「第三届青训营 -后端场」笔记创作活动的的第6篇笔记 对应于第二次课 最近做项目经常用到单元测验 所以做了些补充
GO 协程以及单元测试
Goroutine
- 协程
- 用户态 线程跑多个协程
- 内核态 轻量级进程
- 开启协程
go func - 协程间的通信
- 通道
- 临界区
提倡使用通道来进行通信
- Channel
- 无缓冲通道
make(chan type)- 是一种同步通道 也就是说放入之后生产者阻塞直到消费者消费
- 有缓冲通道
make(chan type, size)- 生产者消费者模型
- 无缓冲通道
// 管道大小不一样 执行速度会有差异
func pro() {
defer close(src)
for i := 0; i < 10; i++ {
src<-i
fmt.Println("第",i,"个数字进入")
}
}
func get() {
for i := 0; i < 10; i++ {
for i:= range src{
fmt.Println("第",i,"个数字输出")
}
}
}
- Lock 锁机制
sync.mutex- 访问临界区资源时 需要加锁
- waitGroup 等待所有线程完成
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
依赖管理
###依赖管理演进
-
GO PATH
- 项目代码直接依赖src下的代码
- go get 下载最新版本的包到src
无法实现多版本控制
-
GO VENDOR
-
GO MODULE
- 自动下载依赖包
- 项目不必放在GOPATH/src内了
- 项目内会生成一个go.mod文件,列出包依赖 还有一个go.sum 确保依赖不会被恶意更改
- 所以来的第三方包会准确的指定版本号
- 对于已经转移的包,可以用replace 申明替换,不需要改代码
- go 会自动查找代码中的包,下载依赖包,并且把具体的依赖关系和版本写入到go.mod和go.sum文件中
- 依赖选择会选择最低的兼容版本
module github.com/Moonlight-Zhao/go-project-example
go 1.16
require (
// 路径 + 空格 +版本号
bou.ke/monkey v1.0.2
github.com/bytedance/gopkg v0.0.0-20220401081311-c38fb59326b7
// 表示非直接依赖
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.3.0 // indirect
github.com/gitstliu/go-id-worker v0.0.0-20190725025543-5a5fe074e612
github.com/golang/protobuf v1.5.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/stretchr/testify v1.7.1
github.com/ugorji/go v1.2.7 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/gin-gonic/gin.v1 v1.3.0
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
测试
- 回归测试
- 手动通过终端回顾一些场景有没有出错
- 集成测试
- 对系统某个功能维度测试有没有出错
- 自动化
- 单元测试
- 开发者对单独模块与函数进行验证
单元测试规则
- 所有测试文件以 _test.go 结尾
- func TestXxx(*testing.T)
- 初始化逻辑放在TestMain中
func TestAddLock(t *testing.T) {
output := Add()
expect := res
//断言
assert.Equal(t,expect,output)
}
func TestMain(m testing.M){
//测试前 初始化操作
code := m.Run()
//测试后 工作
}
- 覆盖率 指代码覆盖率 一般50-60 高要求80以上
- 运行时加参数 --cover
- Mock
- 可以使运行的数据不依赖与实际场景,而使用伪造数据
- 常用包 bou.ke/monkey
func TestProcessFirstLine(t *testing.T) {
firstLine := ProcessFirstLine()
assert.Equal(t, "line00", firstLine)
}
func TestProcessFirstLineWithMock(t *testing.T) {
// 用一个函数替换另一个
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
}
GOLAND自动生成的测试代码解释
GOLAND可以一键生成测试代码,并且可以测试多组数据 还是比较方便的
func TestFollowed1(t *testing.T) {
// 表示参数的结构体
type args struct {
ctx context.Context
userId int64
toUserId int64
}
//测试数据
tests := []struct {
name string
args args
// 结果
want bool
//是否需要错误
wantErr bool
}{
// 添加用例
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
//执行方法
got, err := Followed(tt.args.ctx, tt.args.userId, tt.args.toUserId)
if (err != nil) != tt.wantErr {
t.Errorf("Followed() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Followed() got = %v, want %v", got, tt.want)
}
})
}
}
基准测试
- 用于测试代码性能
- 内置框架提供了基准测试的能力
func BenchmarkSelect(b *testing.B) {
InitServerIndex()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Select()
}
}
func BenchmarkSelectParallel(b *testing.B) {
InitServerIndex()
b.ResetTimer()
// 并行测试
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Select()
}
})
}