乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

    • Golang基础知识

      • 开发环境搭建
      • 常量与变量
      • 基本数据类型
      • 复合数据类型
      • 流程控制
      • 运算符
      • 位运算符详解
      • 指针
      • map
      • 函数
      • defer
      • 类型别名与自定义类型
      • 结构体
      • 接口
      • 包
      • 文件操作
      • 反射
      • 并发
      • socket网络编程
      • HTTP网络编程
      • 单元测试
        • go test 测试工具
        • 测试函数
        • 测试组
        • 子测试
        • 测试覆盖率
      • 基准测试
      • 并发测试
      • 示例函数
      • 性能优化
      • go module
      • 在Go中使用Makefile
      • 部署Go项目
    • Golang进阶知识

    • Golang常用包

  • AIOps

  • 专栏
  • Golang
  • Golang基础知识
乔克
2025-07-13
目录

单元测试

# go test 测试工具

Go 语言中的测试依赖go test命令。编写测试代码和编写普通的 Go 代码过程是类似的,并不需要学习新的语法、规则或工具。

go test 命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。

在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。

类型 格式 作用
测试函数 函数名前缀为 Test 测试程序的一些逻辑行为是否正确
基准函数 函数名前缀为 Benchmark 测试函数的性能
示例函数 函数名前缀为 Example 为文档提供示例文档

go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的 main 包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

# 测试函数

每个测试函数必须导入testing包,测试函数的基本格式(签名)如下:

func TestName(t *testing.T){
    // ...
}
1
2
3

测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头,举几个例子:

func TestAdd(t *testing.T){ ... }
func TestSum(t *testing.T){ ... }
func TestLog(t *testing.T){ ... }
1
2
3

其中参数t用于报告测试失败和附加的日志信息。 testing.T的拥有的方法如下:

func (c *T) Error(args ...interface{})
func (c *T) Errorf(format string, args ...interface{})
func (c *T) Fail()
func (c *T) FailNow()
func (c *T) Failed() bool
func (c *T) Fatal(args ...interface{})
func (c *T) Fatalf(format string, args ...interface{})
func (c *T) Log(args ...interface{})
func (c *T) Logf(format string, args ...interface{})
func (c *T) Name() string
func (t *T) Parallel()
func (t *T) Run(name string, f func(t *T)) bool
func (c *T) Skip(args ...interface{})
func (c *T) SkipNow()
func (c *T) Skipf(format string, args ...interface{})
func (c *T) Skipped() bool
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

例子:

splitString.go

package splitString

import "strings"

// SplitStr ..
func SplitStr(s, sep string) (res []string) {
	// 取索引
	index := strings.Index(s, sep)
	for index >= 0 {
		res = append(res, s[:index])
		s = s[index+1:]
		index = strings.Index(s, sep)
	}
	res = append(res, s)
	return
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

测试用例:

splitStr_test.go

package splitString

import (
	"reflect"
	"testing"
)

func TestSplitStr(t *testing.T){
	// 实际输出结果
	got := SplitStr("a:b:c",":")
	// 期望输出
	want := []string{"a", "b", "c"}
	// 两个做比较
	if !reflect.DeepEqual(got,want){
		t.Errorf("测试失败,期望:%v,实际:%v",want,got)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

运行测试:

PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test
PASS
ok      code.rookieops.com/day05/splitStr       1.362s
1
2
3

# 测试组

所谓的测试组就是将上面一个一个单独的测试函数封装到一个函数中。如下:

package splitStr

import (
	"reflect"
	"testing"
)

func TestSplitStr(t *testing.T) {
	// 定义一个切片,字段为我们需要测试的字段
	type test struct {
		str  string
		sep  string
		want []string
	}
	// 实例化结构体,将我们需要测试的数据实例化
	tests := []test{
		{"a:b:c", ":", []string{"a", "b", "c"}},
		{"abcdbcfwqa", "b", []string{"a", "cd", "cfwqa"}},
		{"啊你好帅啊好帅", "啊", []string{"你好帅", "好帅"}},
	}
	// 循环进行测试
	for _, ts := range tests {
		got := SplitStr(ts.str, ts.sep)
		if !reflect.DeepEqual(got, ts.want) {
			t.Errorf("测试失败,期望:%#v,实际:%#v", ts.want, got)
		}
	}
}

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
28
29

推荐使用%#v的格式化方式。

# 子测试

如果测试用例比较多,需要定位是哪一个测试用例或者只执行某一个测试用例,需要使用子测试。

但是在使用子测试之前,可能会使用如下方法:

package splitStr

import (
	"reflect"
	"testing"
)

func TestSplitStr(t *testing.T) {
	// 定义一个切片,字段为我们需要测试的字段
	type test struct {
		str  string
		sep  string
		want []string
	}
	// 测试用例用map存储
	tests := map[string]test{
		"case1": {"a:b:c", ":", []string{"a", "b", "c"}},
		"case2": {"abcdbcfwqa", "b", []string{"a", "cd", "cfwqa"}},
		"case3": {"啊你好帅啊好帅", "啊", []string{"你好帅", "好帅"}},
	}

	// 循环进行测试
	for name, ts := range tests {
		got := SplitStr(ts.str, ts.sep)
		if !reflect.DeepEqual(got, ts.want) {
			t.Errorf("测试失败,name: %s 期望:%#v,实际:%#v", name, ts.want, got)
		}
	}
}
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
28
29

然后执行结果如下:

PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test -v
=== RUN   TestSplitStr
--- FAIL: TestSplitStr (0.00s)
    splitStr_test.go:26: 测试失败,name: case3 期望:[]string{"你好帅", "好帅"},实际:[]string{"", "你好帅", "好帅"}
FAIL
exit status 1
FAIL    code.rookieops.com/day05/splitStr       3.302s
1
2
3
4
5
6
7

这样可以解决我们的诉求。

但是子测试更加优雅并且可以只执行某个用例,子测试用t.Run()。

代码如下:

package splitStr

import (
	"reflect"
	"testing"
)

func TestSplitStr(t *testing.T) {
	// 定义一个切片,字段为我们需要测试的字段
	type test struct {
		str  string
		sep  string
		want []string
	}
	// 测试用例用map存储
	tests := map[string]test{
		"case1": {"a:b:c", ":", []string{"a", "b", "c"}},
		"case2": {"abcdbcfwqa", "b", []string{"a", "cd", "cfwqa"}},
		"case3": {"啊你好帅啊好帅", "啊", []string{"", "你好帅", "好帅"}},
	}

	// 循环进行测试
	for name, ts := range tests {
		t.Run(name, func(t *testing.T) {
			got := SplitStr(ts.str, ts.sep)
			if !reflect.DeepEqual(got, ts.want) {
				t.Errorf("测试失败,name: %s 期望:%#v,实际:%#v", name, ts.want, got)
			}
		})
	}
}

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
28
29
30
31
32

执行代码如下:

PS E:\DEV\Go\src\code.rookieops.com\day05\splitStr> go test -v
=== RUN   TestSplitStr
=== RUN   TestSplitStr/case1
=== RUN   TestSplitStr/case2
=== RUN   TestSplitStr/case3
--- PASS: TestSplitStr (0.00s)
    --- PASS: TestSplitStr/case1 (0.00s)
    --- PASS: TestSplitStr/case2 (0.00s)
    --- PASS: TestSplitStr/case3 (0.00s)
PASS
ok      code.rookieops.com/day05/splitStr       1.400s
1
2
3
4
5
6
7
8
9
10
11

如果我们要运行某个测试用例,用go test -v -run=TestSplitStr/case3,如下:

 go test -v -run=TestSplitStr/case3
=== RUN   TestSplitStr
=== RUN   TestSplitStr/case3
--- PASS: TestSplitStr (0.00s)
    --- PASS: TestSplitStr/case3 (0.00s)
PASS
ok      code.rookieops.com/day05/splitStr       1.039s
1
2
3
4
5
6
7

# 测试覆盖率

测试覆盖率是你的代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率,也就是在测试中至少被运行一次的代码占总代码的比例。

Go 提供内置功能来检查你的代码覆盖率。我们可以使用go test -cover来查看测试覆盖率。例如:

go test -cover
PASS
coverage: 100.0% of statements
ok      code.rookieops.com/day05/splitStr       1.151s
1
2
3
4

Go 还提供了一个额外的-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件。例如:

go test   -coverprofile c.out

go test   -coverprofile c.out
PASS
coverage: 100.0% of statements
ok      code.rookieops.com/day05/splitStr       1.183s
1
2
3
4

这个文件还可以用 html 查看,如下:

go tool cover -html c.out
1

90b02acaefc978bcb8e27a725af014c1 MD5

上次更新: 2025/07/18, 18:41:03
HTTP网络编程
基准测试

← HTTP网络编程 基准测试→

最近更新
01
2025年,SRE在企业中可以做哪些事
07-18
02
SRE 如何提升自己在团队中的影响力
07-18
03
使用Go开发MCP服务
07-18
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式