跳到主要内容

Go单元测试

Go Package 管理发展史

Go依赖管理是通过Git仓库模式实现,并随着版本的更迭已经逐渐完善。

  • GOPAHT模式

GOPATH目录是所有工程的公共依赖包目录,所有需要编译的go工程的依赖包都放在GOPATH目录下。

  • Vendor特性

为了解决GOPATH模式下,多个工程需要共享GOPATH目录,无法适用于多个工程对于不同版本的依赖包的使用,不便于更新某个依赖包。go1.6之后开启了vendor目录。

  • Go Module包管理

从go1.11以后开始支持Module依赖管理工具,从而实现了依赖包的进行升级更新,在go1.13版本后默认打开

GOPATH为go的开发环境时设置的一个环境变量。

历史版本的go语言开发时,会将代码放在GOPATH目录的src文件下,使用go get命令获取依赖,也会自动下载到GOPATH的src下。

go get github.com/foo/bar

会将代码下载到$GOPATH/src/github.com/foo/bar

- bin,用于存放编译后的可执行文件 go install 编译时生成的可执行文件
- pkg,用于存放编译后的包文件 go install 编译时生成的包文件
- src,放我们以后编写的所有的go代码和依赖
- 项目1
- app.go
- 项目2
- xx.go

Using Go Module

主要公共go.modgo.sum组成,主要包含依赖模块路径定义,以及通过checksum方式进行保证包的安全性。并且可以在GOPATH外进行创建和编译项目。

::: tip 通过go mod init 初始化一个项目 :::

$ 目录 go mod init example.com/hello
go: creating new go.mod: module example.com/hello
package main

import log "github.com/sirupsen/logrus"

func main() {
log.Info("hello")
}

通过go get github.com/sirupsen/logrus可以下载或者更新依赖包,也可以指定版本path@v1.8.1

module example.com/hello

go 1.16

require github.com/sirupsen/logrus v1.8.1

几个关键语法关键字和含义:

  • module:定义当前项目的模块路径
  • go:标识当前模块的go语言版本,仅仅是标识作用
  • require:说明module需要什么版本的依赖
  • exclude:用于从使用中排除一个特定的模块版本
  • replace:替换require中声明的依赖,使用另外的依赖及其版本号
Details
module example.com/hello

go 1.16

require github.com/sirupsen/logrus v1.8.1

exclude github.com/sirupsen/logrus v1.8.0

replace github.com/sirupsen/logrus => github.com/sirupsen/logrus v1.8.2

Go Modules Checksum

为了解决Go Modules的包被篡改安全隐患,Go开发团队在引入go.mod的同时也引入了go.sum文件,用于记录每个依赖包的哈希值,在构建时,如果本地的依赖包hash值与go.sum文件中记录得不一致,则会拒绝构建。

  • go.sum 文件中每行记录由module名、版本和哈希组成,并由空格分开;
  • 在引入新的依赖时,通常会使用go get命令获取该依赖,将会下载该依赖包下载到本地缓存目录$GOPATH/pkg/mod/cache/download,该依赖包为一个后缀为.zip的压缩包,并且把哈希运算同步到go.sum文件中;
  • 在构建应用时,会从本地缓存中查找所有的go.mod中记录的依赖包,并计算本地依赖包的哈希值,然后与go.sum中的记录进行对比,当校验失败,go命令将拒绝构建。

Go Modules Proxy

Go1.13将GOPROXY默认成了中国大陆无法访问的https://proxy.golang.org,所以在国内需要配置代理进行使用。

GOPROXY可以解决一些公司内部的使用问题:

  • 访问公司内网的git server
  • 防止公网仓库变更或者消失,导致线上编译失败或者紧急回退失败
  • 公司审计和安全需要
  • 防止公司内部开发人员配置不当造成import path泄露
  • cache热点依赖,降低公司公网出口宽带
# 配置GOPROXY环境变量
export GOPROXY=https://goproxy.io,direct
# 还可以设置不走 proxy 的私有仓库,多个用逗号相隔(可选)
export GOPRIVATE=git.mycompany.com,github.com/my/private

GOPRIVATE用来控制go命令把哪些仓库看做是私有的仓库,这样的话,可以跳过proxy server的校验检查。

当配置GOPRIVATE时,它的值同时也将作为GONOPROXYGONOSUMDB的默认值,所以当配置GOPRIVATE后将会跳过代理,以及checksum的校验检查。

::: tip 通常推荐配置GOPROXYGOPRIVATE进行使用,GOPRIVATE也可以识别git ssh key进行权限校验。 :::

GOPROXY编译部署

goproxy.io是一个Go Modules开源代理,也可以作为公司内部代理。

下载及编译

git clone https://github.com/goproxyio/goproxy.git
cd goproxy
go build

运行goproxy代理

./goproxy -listen=0.0.0.0:8081 -cacheDir=/tmp/cache -proxy https://goproxy.io -exclude "github.com/private"

goproxy运行配置信息:

  • -cacheDir string

go modules cache dir [指定Go模块的缓存目录]

  • -exclude string

exclude host pattern [proxy模式下指定哪些path不经过上游服务器]

  • -listen string

service listen address [服务监听端口,默认8081]

  • -proxy string

next hop proxy for go modules [指定上游 proxy server, 推荐 goproxy.io]

GoPROXY 访问内网仓库

goproxy配置访问公司内网的git server

  • 用户本地配置GONOSUMDB=github.com/private
  • goproxy server配置exclude 进行排除掉所有的代理仓库
  • goproxy server 配置 ssh key 并且在仓库添加只读权限
  • goproxy server 配置 .gitconfig 把ssh替换成http方式访问
[url "git@github.com:"]
insteadOf = https://github.com/
[url “git@gitlab.com:”]
insteadOf = https://gitlab.com/

Unittest

  • 小型测试带来的优秀的代码质量、良好的异常处理、优雅的错误报告;大中型测试会带来整体产品质量和数据验证。
  • 不同的类型的项目,对测试的需求不同,总体上有一个经验法则,即70/20/10原则:70%是小型测试,20%是中型测试,10%是大型测试。
  • 如果一个项目是面向用户的,拥有较高的集成度,或者用户接口比较复杂,他们就应该有更多的中型和大型测试,例如索引或网络爬虫,则最好有大量的小型测试,中型测试和大型测试的数量要求会少很多。

单元测试的基本要求:

  • 快速
  • 环境一致
  • 任意顺序
  • 并行

基于docker-compose实现跨平台语言环境的容器依赖管理方案,以解决运行unittest场景下的(mysql、redis、mc)容器依赖问题:

  • 本地安装 Docker
  • 无侵入式的环境初始化
  • 快速重置环境
  • 随时随地运行(不依赖外部服务)
  • 语义式API声明资源
  • 真实外部依赖,而非in-process模拟
  • 正确对容器内服务进行健康检测,避免unittest启动时资源还未ready
  • 应该交由app自己来初始化数据,比如db的schema,初始的sql数据等,为了满足测试的一致性,在每次结束后,都会销毁容器。