注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本系列为 Go 进阶训练营 笔记,访问 博客: Go进阶训练营, 即可查看当前更新进度,部分文章篇幅较长,使用 PC 大屏浏览体验更佳。
3 月进度: 05/15 3 月开始会尝试爆更模式,争取做到两天更新一篇文章,如果感兴趣可以拉到文章最下方获取关注方式。
从我们开始开发以来,应该很多人都提到过测试的重要性,而在所有的测试类型当中,以单元测试为代表的单元测试无疑是成本最小,性价比最高的一种,而且有的公司为了保证质量会要求单元测试覆盖率的指标(包括我们)

所以希望看完这篇文章,希望大家可以很快的在我们之前提出的项目结构上进行单元测试的编写,可以做到又快又好。
本文分为两部分,前半段会简单介绍一下 go 的单元测试怎么写,不会有很复杂的技巧,如果已经比较了解可以跳过,后半段会介绍一下我们在项目当中该如何写 “单元测试”
项目结构
1 | |
max.go
1 | |
max_test.go
1 | |
执行结果
1 | |
单元测试文件说明
_test.go结尾的,这样在执行go test的时候才会执行到相应的代码。import testing这个包。Test开头。TestX()的参数是testing.T,我们可以使用该类型来记录错误或者是测试状态。func TestXxx (t *testing.T),Xxx部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如Testintdiv是错误的函数名。testing.T的Error, Errorf, FailNow, Fatal, FatalIf方法,说明测试不通过,调用Log方法用来记录测试的信息。在实际编写单元测试的时候,我们往往需要执行多个测试用例,期望达到更全面的覆盖效果,这时候就需要使用表驱动测试了。
1 | |
执行结果
1 | |
随机执行
上面的例子是按照顺序执行的,单元测试大多随机执行更能够发现一些没有注意到的错误, 如下面的这个例子,利用 map 的特性我们很容易将上面这个例子改造为随机执行的单元测试
1 | |
标准库为我们提供了一个还不错的测试框架,但是没有提供断言的功能,testify 包含了 断言、mock、suite 三个功能,mock 推荐使用官方的 gomock
testify/assert 提供了非常多的方法,这里为大家介绍最为常用的一些,所有的方法可以访问 https://godoc.org/github.com/stretchr/testify/assert 查看
1 | |
我们可以发现,断言方法都会返回一个 bool 值,我们可以通过这个返回值判断断言成功/失败,从而做一些处理
一个例子
1 | |
执行结果, 可以看到输出十分的清晰
1 | |
注意: 请在非项目文件夹执行下面这条命令
1 | |
mockgen 是一个代码生成工具,可以对包或者源代码文件生成指定接口的 Mock 代码
指定源文件
1 | |
指定包路径
1 | |
1 | |
本文只是简单介绍用法,详细使用及 API 说明可以查看官方仓库,[https://github.com/golang/mock](https://github.com/golang/mock)
接下来就是本文的重点,在我们之前提到的 Go 工程化(二) 项目目录结构 当中,如何编写单元测试。虽然这里说的是单元测试,其实后面讲的其实很多不是单元测试,像 repo 层,如果涉及到数据库后面就会讲到我们一般会启动一个真实的数据库来测试,这其实已经算是集成测试了,但是它仍然是轻量级的。
这一层主要处理的 dto 和 do 数据之间的相互转换,本身是不含什么业务逻辑的,目前我们使用的是 http,所以在这一层的测试一般会使用 httptest 来模拟实际请求的测试。然后在对 usecase 层的调用上,我们使用 gomock mock 掉相关的接口,简化我们的测试。如果你不想写的那么麻烦,也可以不用启用 httptest 来测试,直接测试 service 层的代码也是可以的,不过这样的话,service 层的代码测试的内容就没有多少了,也就是看转换数据的时候符不符合预期。
这一层主要完成的测试是
当然如果时间有限的话,这一层的测试也不是必须的,因为接入层相对来说变化也比较快一点,这是说写了单元测试,基本上在测试阶段很少会出现由于参数的问题提交过来的 bug
同样我们直接看一个例子, 首先是 service 层的代码,可以看到逻辑很简单,就是调用了一下,usecase 层的接口
1 | |
再看看单元测试
首先是初始化,之前我们讲到初始化的时候我们一般在 cmd 当中使用 wire 自动生成,但是在单元测试中 wire 并不好用,并且由于单元测试的时候我们的依赖项其实没有真实的依赖项那么复杂我们只需要关心当前这一层的依赖即可,所以一般在单元测试的时候我都是手写初始化
一般会向下面这样,使用一个 struct 包装起来,因为在后面像是 mock 的 usecase 还需要调用
1 | |
实际的测试,这一块主要是为了展示一个完整的单元测试所以贴的代码稍微长了一些,后面的两层具体的单元测试代码都大同小异,我就不再贴了,主要的思路就是把依赖的接口都用 gomock mock 掉,这样实际写单元测试代码的时候就会比较简单。
1 | |
usecase 是主要的业务逻辑,所以一般写单元测试的时候都应该先写这一层的单远测试,而且这一层我们没有任何依赖,只需要把 repo 层的接口直接 mock 掉就可以了,是非常纯净的一层,其实也就这一层的单元测试才是真正的单元测试
repo 层我们一般依赖 mysql 或者是 redis 等数据库,在测试的时候我们可以直接启动一个全新的数据库用于测试即可。
直接使用 docker run 对应的数据库就可以了
我们的 ci cd 是使用的 gitlab,gitlab 有一个比较好用的功能是指定 service,只需要指定对应的数据库镜像我们就可以在测试容器启动的时候自动启动对应的测试数据库容器,并且每一次都是全新的空数据库。我们只需要每次跑单元测试的时候先跑一下数据库的 migration 就可以了。
下面给出一个配置示例
1 | |
单元测试的介绍就到这里了,这篇文章从单元测试的基本写法,再到我们在项目当中如何写单元测试都简单介绍了一下,希望你看了这篇文章能有所收获。
同时我们 Go 工程化 这一章节的内容也接近尾声了,整理的材料也挺多的,下一篇就是这一节的最后一篇文章,讲一讲我在真实改造一个项目的时候遇到的一些问题和解决方案。
注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本系列为 Go 进阶训练营 笔记,访问 博客: Go进阶训练营, 即可查看当前更新进度,部分文章篇幅较长,使用 PC 大屏浏览体验更佳。
3 月进度: 05/15 3 月开始会尝试爆更模式,争取做到两天更新一篇文章,如果感兴趣可以拉到文章最下方获取关注方式。
从我们开始开发以来,应该很多人都提到过测试的重要性,而在所有的测试类型当中,以单元测试为代表的单元测试无疑是成本最小,性价比最高的一种,而且有的公司为了保证质量会要求单元测试覆盖率的指标(包括我们)

所以希望看完这篇文章,希望大家可以很快的在我们之前提出的项目结构上进行单元测试的编写,可以做到又快又好。
本文分为两部分,前半段会简单介绍一下 go 的单元测试怎么写,不会有很复杂的技巧,如果已经比较了解可以跳过,后半段会介绍一下我们在项目当中该如何写 “单元测试”
项目结构
1 | |
max.go
1 | |
max_test.go
1 | |
执行结果
1 | |
单元测试文件说明
_test.go结尾的,这样在执行go test的时候才会执行到相应的代码。import testing这个包。Test开头。TestX()的参数是testing.T,我们可以使用该类型来记录错误或者是测试状态。func TestXxx (t *testing.T),Xxx部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如Testintdiv是错误的函数名。testing.T的Error, Errorf, FailNow, Fatal, FatalIf方法,说明测试不通过,调用Log方法用来记录测试的信息。在实际编写单元测试的时候,我们往往需要执行多个测试用例,期望达到更全面的覆盖效果,这时候就需要使用表驱动测试了。
1 | |
执行结果
1 | |
随机执行
上面的例子是按照顺序执行的,单元测试大多随机执行更能够发现一些没有注意到的错误, 如下面的这个例子,利用 map 的特性我们很容易将上面这个例子改造为随机执行的单元测试
1 | |
标准库为我们提供了一个还不错的测试框架,但是没有提供断言的功能,testify 包含了 断言、mock、suite 三个功能,mock 推荐使用官方的 gomock
testify/assert 提供了非常多的方法,这里为大家介绍最为常用的一些,所有的方法可以访问 https://godoc.org/github.com/stretchr/testify/assert 查看
1 | |
我们可以发现,断言方法都会返回一个 bool 值,我们可以通过这个返回值判断断言成功/失败,从而做一些处理
一个例子
1 | |
执行结果, 可以看到输出十分的清晰
1 | |
注意: 请在非项目文件夹执行下面这条命令
1 | |
mockgen 是一个代码生成工具,可以对包或者源代码文件生成指定接口的 Mock 代码
指定源文件
1 | |
指定包路径
1 | |
1 | |
本文只是简单介绍用法,详细使用及 API 说明可以查看官方仓库,[https://github.com/golang/mock](https://github.com/golang/mock)
接下来就是本文的重点,在我们之前提到的 Go 工程化(二) 项目目录结构 当中,如何编写单元测试。虽然这里说的是单元测试,其实后面讲的其实很多不是单元测试,像 repo 层,如果涉及到数据库后面就会讲到我们一般会启动一个真实的数据库来测试,这其实已经算是集成测试了,但是它仍然是轻量级的。
这一层主要处理的 dto 和 do 数据之间的相互转换,本身是不含什么业务逻辑的,目前我们使用的是 http,所以在这一层的测试一般会使用 httptest 来模拟实际请求的测试。然后在对 usecase 层的调用上,我们使用 gomock mock 掉相关的接口,简化我们的测试。如果你不想写的那么麻烦,也可以不用启用 httptest 来测试,直接测试 service 层的代码也是可以的,不过这样的话,service 层的代码测试的内容就没有多少了,也就是看转换数据的时候符不符合预期。
这一层主要完成的测试是
当然如果时间有限的话,这一层的测试也不是必须的,因为接入层相对来说变化也比较快一点,这是说写了单元测试,基本上在测试阶段很少会出现由于参数的问题提交过来的 bug
同样我们直接看一个例子, 首先是 service 层的代码,可以看到逻辑很简单,就是调用了一下,usecase 层的接口
1 | |
再看看单元测试
首先是初始化,之前我们讲到初始化的时候我们一般在 cmd 当中使用 wire 自动生成,但是在单元测试中 wire 并不好用,并且由于单元测试的时候我们的依赖项其实没有真实的依赖项那么复杂我们只需要关心当前这一层的依赖即可,所以一般在单元测试的时候我都是手写初始化
一般会向下面这样,使用一个 struct 包装起来,因为在后面像是 mock 的 usecase 还需要调用
1 | |
实际的测试,这一块主要是为了展示一个完整的单元测试所以贴的代码稍微长了一些,后面的两层具体的单元测试代码都大同小异,我就不再贴了,主要的思路就是把依赖的接口都用 gomock mock 掉,这样实际写单元测试代码的时候就会比较简单。
1 | |
usecase 是主要的业务逻辑,所以一般写单元测试的时候都应该先写这一层的单远测试,而且这一层我们没有任何依赖,只需要把 repo 层的接口直接 mock 掉就可以了,是非常纯净的一层,其实也就这一层的单元测试才是真正的单元测试
repo 层我们一般依赖 mysql 或者是 redis 等数据库,在测试的时候我们可以直接启动一个全新的数据库用于测试即可。
直接使用 docker run 对应的数据库就可以了
我们的 ci cd 是使用的 gitlab,gitlab 有一个比较好用的功能是指定 service,只需要指定对应的数据库镜像我们就可以在测试容器启动的时候自动启动对应的测试数据库容器,并且每一次都是全新的空数据库。我们只需要每次跑单元测试的时候先跑一下数据库的 migration 就可以了。
下面给出一个配置示例
1 | |
单元测试的介绍就到这里了,这篇文章从单元测试的基本写法,再到我们在项目当中如何写单元测试都简单介绍了一下,希望你看了这篇文章能有所收获。
同时我们 Go 工程化 这一章节的内容也接近尾声了,整理的材料也挺多的,下一篇就是这一节的最后一篇文章,讲一讲我在真实改造一个项目的时候遇到的一些问题和解决方案。