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都是第三方的库和框架(生态庞大!)

相关推荐
Rverdoser6 分钟前
在 Centos7 上部署 ASP.NET 8.0 + YOLOv11 的踩坑实录
后端·yolo·asp.net
二十雨辰10 分钟前
[Java基础]反射技术
java·开发语言·算法
wrjwww15 分钟前
【Git学习笔记】Git常用命令
笔记·git·学习
XuanRanDev16 分钟前
【构建工具】Gradle Kotlin DSL中的大小写陷阱:BuildConfigField
android·开发语言·kotlin
学途路漫漫18 分钟前
怎么修改node_modules里的文件,怎么使用patch-package修改node_modules的文件,怎么修改第三方库原文件。
开发语言·javascript·ecmascript
灰色人生qwer20 分钟前
SpringBoot项目注入 traceId 来追踪整个请求的日志链路
java·spring boot·后端·日志·slf4j·链路追踪
Asthenia041230 分钟前
MQ项目接入Retry实践——构造器模式+命令模式+模版方法模式
后端
我命由我1234537 分钟前
34.Java 阻塞队列(阻塞队列架构、阻塞队列分类、阻塞队列核心方法)
java·服务器·开发语言·后端·架构·java-ee·后端开发
猿周LV41 分钟前
JUC (java. util.concurrent) 的常见类及创建新线程的方法等 [Java EE 初阶]
java·开发语言·java-ee
m0_748248941 小时前
猿创征文 【高级篇】Java 进阶之JVM实战
java·开发语言·jvm