GO进阶 | 青训营笔记

121 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第6篇笔记 对应于第二次课 最近做项目经常用到单元测验 所以做了些补充

GO 协程以及单元测试

Goroutine

  • 协程
    • 用户态 线程跑多个协程
    • 内核态 轻量级进程
  • 开启协程 go func
  • 协程间的通信
    • 通道
    • 临界区 提倡使用通道来进行通信 image.png
  • 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 无法实现多版本控制 image.png
  • GO VENDOR

  • GO MODULE

    1. 自动下载依赖包
    2. 项目不必放在GOPATH/src内了
    3. 项目内会生成一个go.mod文件,列出包依赖 还有一个go.sum 确保依赖不会被恶意更改
    4. 所以来的第三方包会准确的指定版本号
    5. 对于已经转移的包,可以用replace 申明替换,不需要改代码
    6. go 会自动查找代码中的包,下载依赖包,并且把具体的依赖关系和版本写入到go.mod和go.sum文件中
    7. 依赖选择会选择最低的兼容版本

image.png

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()
      }
   })
}

image.png