单元测试

单元测试

1. 创建单元测试

  • 测试文件以_test.go结尾:每一个go文件,都有一个属于自己的单元测试文件,命名为xxx_test.go。例如:publish_post.go文件的单元测试文件为publish_post_test.go

  • func TestXxx(*testing.T):每一个函数所对应的测试函数命名为TestXxx,参数为*testing.T。例如:PublicPost函数,其测试函数为func TestPublishPost(t *testing.T)

  • 初始化逻辑放到TestMain中:当前包下的所有测试函数的初始化逻辑,都放到TestMain函数中,再调用Run函数执行测试,再进行资源的释放等操作。

func TestMain(m *testing.M) {

//测试前:数据装载,配置初始化等前置工作

code := m.Run()

//测试后:释放资源等收尾工作

os.Exit(code)
}

2. 单元测试例子

2.1 情景假设

​ 假设有函数HelloTom,应该返回string:“Tom”

func HelloTom() string {
return "Jerry"
}

​ 那么针对该函数的测试应为:

func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
if output != expectOutput {
t.Errorf("Expect %s do not match actual %s", expectOutput, output)
}
}

2.2 执行测试

​ 使用go test命令执行单元测试。

go test [flags] [packages]

3. 单元测试框架-assert

​ 有现成的第三方包提供单元测试框架。例如:

import(
"github.com/strechr/testify/assert"
"testing"
)

func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
assert.Equal(t, expectOutput, output)
}

4. 单元测试覆盖率

​ 单元测试的覆盖率是衡量单元测试质量的一个重要指标。使用时,在go test后加上--cover的参数即可在执行单元测试时同时计算出覆盖率。

go test judgement_test.go judgement.go --cover

5. Mock

​ 为了保证单元测试的稳定性:减少单元测试受外部影响(文件丢失,网络不稳定等),常使用Mock的方法对函数进行打桩。

5.1 情景假设

​ 假设需要读取一个文件的一行,并将其中的字符串11改成00

func ReadFirstLine() string {
open, err := os.Open("log")
defer open.Close()
if err!=nil {
return ""
}
scanner := bufio.NewScanner(open)
for scanner.Scan() {
return scanner.Text()
}
return ""
}

func ProcessFirstLine() string {
line := ReadFirstLine()
destLine := strings.ReplaceAll(line, "11", "00")
return destLine
}

​ 这是它的单元测试:

func TestProcessFirstLine(t *testing.T) {
firstLine := ProcessFirstLine()
assert.Equal(t, "line00", firstLine)
}

​ 这个函数的测试,依赖于log文件中的行字符串为“line11"

5.2 使用Mock

​ 快速Mock函数可以做到:

  • 为一个函数打桩

  • 为一个方法打桩

5.2.1 monkey介绍

​ 假设使用的是monkey库:https://github.com/bouk/monkey。它包含两个方法:Patch和UnPatch。

//将target的函数执行换成replacement的函数执行
func Patch(target, replacement interface{}) *PatchGuard {
t := reflect.valueOf(target)
r := reflect.valueOf(replacement)
patchValue(t, r)

return &PatchGuard(t, r)
}

//移除patch函数的效果
func Unpatch(target interface{}) bool {
return unpatchValue(reflect.ValueOf(target))
}

5.2.2 monkey使用

​ 此处对ReadFirstLine函数进行打桩操作,使其摆脱依赖本地文件的影响:

func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
asssert(t, "line000", line)
}

6. 基准测试框架

​ 基准测试是指对一段代码的性能和CPU使用情况进行分析测试。