不得不说,学这个之前我只会APIfox点一点……

  • 回归测试:质量保证人员手动测试项目可用性(刷抖音、看评论)top1
  • 集成测试:对系统功能的测试(对暴露的接口自动化测试)top2
  • 单元测试:开发者对单独的函数模块测试 top3

单元测试

规则

  • 所有测试文件都以 _test.go 结尾
  • 测试函数写成 func TextXxx(t *testing.T)
  • 初始化逻辑放到 TestMain 中(准备测试的数据->跑测试->释放资源)
go
1
2
3
└─test
print.go
print_test.go

如果要测试print.go中的函数输出是否正确

go
1
2
3
4
5
6
// print.go
package test

func HelloTom() string {
return "John"
}

然后建立print_test.go,其实这个时候会发现这个吉祥物就有点不一样,而且整个goland文件有入口可以运行了,而不是只能从main.go进入,下面就填写Testxxxx(T &testint.T){}

最后三角形点一点,记得撰写一下测试逻辑

go
1
2
3
4
5
6
7
8
9
10
package test

import "testing"

func TestHelloTom(t *testing.T) {
want := "Tom"
if got := HelloTom(); got != want {
t.Errorf("HelloTom() = %q, want %q", got, want)
}
}

测试结果:

go
1
2
3
4
5
6
7
8
=== RUN   TestHelloTom
print_test.go:8: HelloTom() = "John", want "Tom"
--- FAIL: TestHelloTom (0.00s)

FAIL


Process finished with the exit code 1

覆盖率

这是一个标准,来判断你的测试是否够格

按照刚才的东西重新写一个案例

go
1
2
3
4
5
6
7
8
9
// JudgePassLine.go
package test

func JudgePassLine(score int) bool {
if score >= 60 {
return true
}
return false
}
go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package test

import "testing"

func TestJudgePassLineTrue2(t *testing.T) {
if !JudgePassLine(80) {
t.Error("The score is less than 60, but it is judged as pass")
}
}

func TestJudgePassLineFalse2(t *testing.T) {
if JudgePassLine(50) {
t.Error("The score is greater than 60, but it is judged as fail")
}
}

绿色三角形中有几个选项,有一个选项是RUN "XXX" WITH COVERAGE

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// testing result
package test

import "testing"

func TestJudgePassLineTrue2(t *testing.T) {
if !JudgePassLine(80) {
t.Error("The score is less than 60, but it is judged as pass")
}
}

func TestJudgePassLineFalse2(t *testing.T) {
if JudgePassLine(50) {
t.Error("The score is greater than 60, but it is judged as fail")
}
}
go
1
2
3
4
5
6
7
8
9
10
// print:
=== RUN TestJudgePassLineTrue2
--- PASS: TestJudgePassLineTrue2 (0.00s)
=== RUN TestJudgePassLineFalse2
--- PASS: TestJudgePassLineFalse2 (0.00s)
PASS

coverage: 100.0% of statements in ../../today/...

Process finished with the exit code 0

单元测试 Tips:

  • 一般覆盖率:50%~60%,较高覆盖率:80%+
  • 测试分支相互独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

文件处理

go
1
2
3
4
5
6
7
8
9
10
// file 
line11
line22
line33
line44
line55




go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// file.go
package test

import (
"bufio"
"os"
"strings"
)

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

func ProcessFirstLine() string {
line := ReadFirstLine()
destLine := strings.ReplaceAll(line, "11", "00")
return destLine
}
go
1
2
3
4
5
6
7
8
9
10
11
// file_test.go
package test

import "testing"

func TestReadFirstLine(t *testing.T) {
got := ProcessFirstLine()
if got != "line00" {
t.Errorf("ReadFirstLine() = %q; want 11", got)
}
}

这个测试依赖于log.txt,但是实际中log无法访问又怎么办?

Mock测试

Mock 就是打桩,在测试时使用一个函数或方法替换另一个函数或方法(在运行时替换函数的指针)

例如在上面使用 ReadFirstLine() 来读取数据,而我们可以用一个函数生成数据,然后替换掉那个函数

常见的用于实现 Mock 的包是 monkey

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package test

import "testing"
import "bou.ke/monkey"

func TestReadFirstLine(t *testing.T) {
got := ProcessFirstLine()
if got != "line00" {
t.Errorf("ReadFirstLine() = %q; want 11", got)
}
}

// 在这个测试用例中,TestProcessFirstLineWithMock 函数首先使用 monkey.Patch 方法替换掉 ReadFirstLine 函数。
//ReadFirstLine 函数原本应该返回文件的第一行,但在这个测试中,我们用 monkey 替换它,使其返回一个固定的字符串 "line110"。
func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
got := ProcessFirstLine()
if got != "line000" {
t.Errorf("ProcessFirstLine() = %q; want 11", got)
}
}

基准测试

Benchmark……..