不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • GO基础

    • 01.GO介绍与安装
    • 02.GO基本语法
    • 03.内置运算符
    • 04.基本数据类型
    • 05.数字类型
    • 06.字符串类型
    • 07.数组
    • 08.切片
    • 09.map
    • 10.指针
    • 11.结构体
    • 12.interface接口
    • 13.循环
    • 14.包管理工具go mod
    • 15.单元测试
      • 01.单元测试
        • 0、单元测试规范
        • 1、普通方法单元测试
        • 2、结构体方法单元测试
        • 3、子测试(Subtests)
        • 1、一般子测试
        • 2、推荐写法
        • 4、setup 和 teardown
    • 16.go规范
  • 面向对象

  • 并发编程

  • 常用库

  • 数据库操作

  • Beego框架

  • Beego商城

  • GIN框架

  • GIN论坛

  • 微服务

  • 设计模式

  • Go
  • GO基础
xiaonaiqiang
2024-02-07
目录

15.单元测试

# 01.单元测试

# 0、单元测试规范

  • 单元测试文件名命名规范为 example_test.go。
  • 测试用例的函数名称必须以 Test 开头,例如 TestExample。
  • 如果存在 func Foo,单测函数可以带下划线,为 func Test_Foo。
  • 如果存在 func (b *Bar) Foo,单测函数可以为 func TestBar_Foo。
  • 单测文件行数限制是普通文件的2倍,即1600行。单测函数行数限制也是普通函数的2倍,即为160行。
  • 圈复杂度、列数限制、 import 分组等其他规范细节和普通文件保持一致。
  • 由于单测文件内的函数都是不对外的,所有可导出函数可以没有注释,但是结构体定义时尽量不要导出。
  • 每个重要的可导出函数都要首先编写测试用例,测试用例和正规代码一起提交方便进行回归测试。

# 1、普通方法单元测试

  • Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾

  • 比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 Add 和 Mul 函数

  • 那么应该新建 calc_test.go 作为测试文件

  • 文件结构

example/
   |--calc.go
   |--calc_test.go
1
2
3
  • calc.go
package main

func Add(a int, b int) int {
    return a + b
}

func Mul(a int, b int) int {
    return a * b
}
1
2
3
4
5
6
7
8
9
  • calc_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
	if ans := Add(1, 2); ans != 3 {
		t.Errorf("1 + 2 expected be 3, but %d got", ans)
	}

	if ans := Add(-10, -20); ans != -30 {
		t.Errorf("-10 + -20 expected be -30, but %d got", ans)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • go test -v,-v 参数会显示每个用例的测试结果,另外 -cover 参数可以查看覆盖率
    • 可以用 -run 参数指定,该参数支持通配符 *,和部分正则表达式,例如 ^、$
$ go test -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      days-test/example       0.006s
1
2
3
4
5

# 2、结构体方法单元测试

  • bar.go
package main

type Bar struct {
}

func (b *Bar) Foo() string {
    return "Hello, World!"
}
1
2
3
4
5
6
7
8
  • bar_test.go
package main

import (
    "testing"
)

func TestBar_Foo(t *testing.T) {
    // 创建 Bar 实例
    b := &Bar{}

    // 调用方法
    result := b.Foo()

    // 预期结果
    expected := "Hello, World!"

    // 检查结果是否符合预期
    if result != expected {
        t.Errorf("Got %s, expected %s", result, expected)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 3、子测试(Subtests)

  • 子测试是 Go 语言内置支持的,可以在某个测试用例中,根据测试场景使用 t.Run创建不同的子测试用例
  • 之前的例子测试失败时使用 t.Error/t.Errorf,这个例子中使用 t.Fatal/t.Fatalf
  • 区别在于前者遇错不停,还会继续执行其他的测试用例,后者遇错即停

# 1、一般子测试

package main

import "testing"

func TestMul(t *testing.T) {
	t.Run("pos", func(t *testing.T) {
		if Mul(2, 3) != 6 {
			t.Fatal("fail")
		}

	})
	t.Run("neg", func(t *testing.T) {
		if Mul(2, -3) != -6 {
			t.Fatal("fail")
		}
	})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 运行某个测试用例的子测试
$ go test -run TestMul/pos -v
=== RUN   TestMul
=== RUN   TestMul/pos
--- PASS: TestMul (0.00s)
    --- PASS: TestMul/pos (0.00s)
PASS
ok      days-test/example       0.005s
1
2
3
4
5
6
7

# 2、推荐写法

  • 新增用例非常简单,只需给 cases 新增一条测试数据即可。
  • 测试代码可读性好,直观地能够看到每个子测试的参数和期待的返回值。
  • 用例失败时,报错信息的格式比较统一,测试报告易于阅读。
package main

import "testing"

func TestMul(t *testing.T) {
	cases := []struct {
		Name           string
		A, B, Expected int
	}{
		{"pos", 2, 3, 6},
		{"neg", 2, -3, -6},
		{"zero", 2, 0, 0},
	}

	for _, c := range cases {
		t.Run(c.Name, func(t *testing.T) {
			if ans := Mul(c.A, c.B); ans != c.Expected {
				t.Fatalf("%d * %d expected %d, but %d got", c.A, c.B, c.Expected, ans)
			}
		})
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 4、setup 和 teardown

  • 如果在同一个测试文件中,每一个测试用例运行前后的逻辑是相同的,一般会写在 setup 和 teardown 函数中
  • 在这个测试文件中,包含有2个测试用例,Test1 和 Test2。
  • 如果测试文件中包含函数 TestMain,那么生成的测试将调用 TestMain(m),而不是直接运行测试。
  • 调用 m.Run() 触发所有测试用例的执行,并使用 os.Exit() 处理返回的状态码,如果不为0,说明有用例失败。
  • 因此可以在调用 m.Run() 前后做一些额外的准备(setup)和回收(teardown)工作。
package main

import (
	"fmt"
	"os"
	"testing"
)

func setup() {
	fmt.Println("Before all tests")
}

func teardown() {
	fmt.Println("After all tests")
}

func Test1(t *testing.T) {
	fmt.Println("I'm test1")
}

func Test2(t *testing.T) {
	fmt.Println("I'm test2")
}

func TestMain(m *testing.M) {
	setup()
	code := m.Run()
	teardown()
	os.Exit(code)
}
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
上次更新: 2024/3/13 15:35:10
14.包管理工具go mod
16.go规范

← 14.包管理工具go mod 16.go规范→

最近更新
01
04.数组双指针排序_子数组
03-25
02
08.动态规划
03-25
03
06.回溯算法
03-25
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式