Go Test 单元测试简明教程

如何写好单元测试

1. 单元测试的重要性

帮助我们在开发过程中及时发现问题,避免提交之后导致应用崩溃或者是服务宕机,尤其是在多人合作和项目频繁变动的情况。提高代码质量和开发效率。

2. 写好测试用例的步骤

**学会写单元测试:**例如测试函数、性能基准、简洁的代码 和 mock 数据库访问等

**写可测试的代码:**高内聚、低耦合的代码更容易进行单元测试。如果一个代码很难进行测试,考虑重构使其更容易测试。

3.提高测试效率

在开发过程中,通过写单元测试用例,可以及时确保代码的正确性,减少调试和集成时不必要的麻烦。

一个简单的例子

Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾 。比如,当前 package 有 calc.go 一个文件,我们想测试 calc.go 中的 AddMul 函数,那么应该新建 calc_test.go 作为测试文件。

|---------------|---------------------------------------------|
| 1 2 3 | example/ |--calc.go |--calc_test.go |

子测试

Go 语言内置支持子测试,可以通过 t.Run 创建不同的子测试用例。子测试的优点是能够在一个大的测试用例中,针对不同的场景进行多次测试,而每个测试又是独立的

表驱动测试(Table-driven Tests)

帮助函数(helpers)

【测试前后】setup 和 teardown

如果在同一个测试文件中,每一个测试用例运行前后的逻辑是相同的 ,一般会写在 setup 和 teardown 函数中。例如执行前需要实例化待测试的对象,如果这个对象比较复杂,很适合将这一部分逻辑提取出来;执行后,可能会做一些资源回收类的工作,例如关闭网络连接,释放文件等。标准库 testing 提供了这样的机制:

Go 复制代码
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) // 退出时传递 m.Run() 的返回状态码
}
  • 在这个测试文件中,包含有2个测试用例,Test1Test2
  • 如果测试文件中包含函数 TestMain,那么生成的测试将调用 TestMain(m),而不是直接运行测试。
  • 调用 m.Run() 触发所有测试用例的执行 ,并使用 os.Exit() 处理返回的状态码,如果不为0,说明有用例失败。
  • 因此可以在调用 m.Run() 前后做一些额外的准备(setup)和回收(teardown)工作

执行 go test,将会输出

复制代码
$ go test
Before all tests
I'm test1
I'm test2
PASS
After all tests
ok      example 0.006s
  • TestMain 允许我们在测试执行之前和之后做额外的工作。
  • 通过 setupteardown 函数,我们可以避免在每个测试用例中重复相同的逻辑,增加代码的可维护性。
  • 使用 m.Run() 来执行所有测试用例,并通过 os.Exit() 处理退出状态码

网络测试


Go 复制代码
import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestConn(t *testing.T) {
	// 创建一个模拟的 HTTP 请求对象,方法为 GET,路径为 "/foo"
	req := httptest.NewRequest("GET", "http://example.com/foo", nil)
	// 创建一个模拟的 HTTP 响应记录器
	w := httptest.NewRecorder()
	// 调用目标 handler,传入模拟的请求和响应对象
	helloHandler(w, req)
	// 读取响应体
	bytes, _ := ioutil.ReadAll(w.Result().Body)

	// 验证返回值是否正确
	if string(bytes) != "hello world" {
		t.Fatal("expected hello world, but got", string(bytes))
	}
}

Benchmark 基准测试

Go 语言中的基准测试用于衡量代码执行的性 能。基准测试通常用于评估某个功能的执行效率,比如函数的执行时间、内存使用情况等。

在 Go 语言中,基准测试通过 testing 包提供的 b *testing.B 参数来进行。你可以通过定义一个以 Benchmark 开头的函数来实现基准测试

基准测试对于性能优化和评估代码效率非常重要,尤其是在处理高并发或高负载的应用时,使用合适的基准测试方法可以帮助你更好地了解代码的性能瓶颈。

Java和go单元测试的异同

一句话,go语言内置很多单元测试库,可以实现很多功能,Java都是第三方的库和框架(生态庞大!)

相关推荐
程序员爱钓鱼几秒前
Go语言并发模型与模式:Worker Pool 模式
后端·go·排序算法
Victor3561 分钟前
MySQL(66)如何进行慢查询日志分析?
后端
程序小武1 分钟前
深入理解Python内置模块及第三方库的使用与管理
后端
陈随易3 分钟前
2025年100个产品计划之第12个(杰森排序) - 对 JSON 属性进行排序
前端·后端·程序员
Lx3525 分钟前
LIKE查询中索引有效利用的前缀匹配策略
后端·sql·oracle
三气归来5 分钟前
2. 内置模块之http模块
javascript·后端
FogLetter6 分钟前
🧙‍♂️ 魔法笔记:JavaScript 词法作用域与闭包的神秘世界
javascript·后端
小璐乱撞6 分钟前
从原理到落地:MCP在Spring AI中的工程实践
后端
库森学长7 分钟前
Kafka为什么这么快?
后端·面试·kafka
Hockor8 分钟前
写给前端的 Python 教程四(列表/元组)
前端·后端·python