如何写好单元测试
1. 单元测试的重要性
帮助我们在开发过程中及时发现问题,避免提交之后导致应用崩溃或者是服务宕机,尤其是在多人合作和项目频繁变动的情况。提高代码质量和开发效率。
2. 写好测试用例的步骤
**学会写单元测试:**例如测试函数、性能基准、简洁的代码 和 mock 数据库访问等
**写可测试的代码:**高内聚、低耦合的代码更容易进行单元测试。如果一个代码很难进行测试,考虑重构使其更容易测试。
3.提高测试效率
在开发过程中,通过写单元测试用例,可以及时确保代码的正确性,减少调试和集成时不必要的麻烦。
一个简单的例子
Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go
结尾 。比如,当前 package 有 calc.go
一个文件,我们想测试 calc.go
中的 Add
和 Mul
函数,那么应该新建 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个测试用例,
Test1
和Test2
。 - 如果测试文件中包含函数
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
允许我们在测试执行之前和之后做额外的工作。- 通过
setup
和teardown
函数,我们可以避免在每个测试用例中重复相同的逻辑,增加代码的可维护性。 - 使用
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都是第三方的库和框架(生态庞大!)
