补一下跟青训营学习的过程
单元测试
- 单元测试位于测试的最底层,是开发者对于单独的模块进行测试,覆盖率最大,测试成本最低,但是决定代码质量最重要的一环。通过测试单元的输出与期望值进行校对从而验证代码的正确性,从而保证新旧代码的互不影响与程序的正常运行。
单元测试规则
-
测试文件以_test.go结尾
-
测试函数以Test开头
-
测试函数的参数为t *testing.T
-
测试函数的返回值为void
-
初始化逻辑放到TestMain函数中
-
测试函数的执行顺序是随机的
-
测试函数的执行顺序是按照测试文件的顺序执行的
-
测试函数的执行顺序是按照测试函数的顺序执行的
- 演示
func TestMain(m \*testing.M) {
// 测试前:数据装载、配置初始化
code := m.Run()
// 测试后:数据清理、配置还原
os.Exit(code)
}
package unittest
import (
"testing"
)
func HelloTom() string {
return "Jerry"
}
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectedOutput := "Tom"
if output != expectedOutput {
t.Errorf("Expected output %s, but got %s.", expectedOutput, output)
}
}
// 命令行执行go test -v hellotom_test.go
预期执行结果
单元测试-assert
除去使用简单的'!='来判断,也同样可以使用第三方库testify中的 assert 操作
直接导入相应的库
import (
"testing"
"github.com/stretchr/testify/assert"
)
在testify导入时可能会报错could not import github.com/stretchr/testify/assert (no required module...),此时就需要通过go get指令来获得相应的库
命令行输入
go get -u -v github.com/stretchr/testify/assert
终端会有如下响应
go: downloading github.com/stretchr/testify v1.9.0
go: downloading github.com/davecgh/go-spew v1.1.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: added github.com/davecgh/go-spew v1.1.1
go: added github.com/pmezard/go-difflib v1.0.0
go: added github.com/stretchr/testify v1.9.0
go: added gopkg.in/yaml.v3 v3.0.1
之后就可以使用assert操作完成相应的单元测试
- 演示
package unittest
import (
"testing"
"github.com/stretchr/testify/assert"
)
func HelloTom() string {
return "Jerry"
}
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectedOutput := "Tom"
assert.Equal(t, expectedOutput, output, "They shoule be equal")
}
// 命令行执行go test hellotom_test.go
预期执行结果
可以看到经过assert操作,命令行可以返回更加详细的报错信息,例如我们预期输出(expected)为"Tom",实际输出(actual)为"Jerry",此时就需要进行相应修改
单元测试-覆盖率
- 衡量代码是否经过了足够的测试
- 评价项目的测试水准
- 评估项目是否达到了高水准测试等级
演示
package unittest
func JudgePassLine(score int16) bool {
if score >= 60 {
return true
}
return false
}
package unittest
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestJudgePassLine(t *testing.T) {
isPass := JudgePassLine(70)
assert.Equal(t, true, isPass)
}
// 命令行执行go test judgepassline_test.go judgepassline.go --cover
预期输出
其中66.7%的覆盖率是因为在judgepassline.go中从if条件判断开始执行,直到return true后便不再执行,没有执行最后的return false,也就是说三行代码执行了两行,所以测试覆盖率为2/3=66.7%。
实际应用
在跟青训营做一个基于Gin高性能的go web框架,主要实现的功能是对某一话题下回帖内容的查询。在进行相关repository实现的内存索引过程中,涉及到了初始化回帖列表索引数据函数initPostIndexMap,它的作用是将post按照parent_id顺序添加在post列表中,在对post列表进行初始化添加时,缺少了一句相关代码,post列表理想初始化情况为每一个topicId下对应5个post,而因为缺少了一行代码,导致了post列表初始化后仅有一个元素。测试函数如下图所示
首先通过TestMain来测试repository是否能正确初始化,然后通过TestQueryPageInfo来测试帖子的查询功能是否正确,以id=1来测试,assert.NotEqual()来判断pageInfo是否正确初始化,再通过assert.Equal()来判断id=1的topic下postList是否正确初始化,预期值为5,然而通过下图可以看出assert操作测试得到结果为1,证明我们postList初始化发生了错误
此时不信邪,继续开发的话,直到最后项目的repository、service、controller还有最后的外部接口都编写完毕后,运行项目,通过在命令行输入相关curl命令去访问该页面信息,会得到如下反馈
可以看到,确实有topic以及相关postList的信息反馈,但是postList的元素仅有一个,和我们之前通过TestQueryPageInfo来测试初始化回帖列表元素是否正常运行的结果是对应的(即上一张图中的expected:5 actual:1) ,后续就是通过delve对代码的调试,让get命令成功读取到5个post
在初学的小项目中,assert可以实现一些核心功能的测试,图片来看是非常直观的,也算是加深了对于单元测试的一些理解