找回密码
 立即注册
首页 业界区 业界 《Go 单元测试从入门到覆盖率提升》(二) ...

《Go 单元测试从入门到覆盖率提升》(二)

谯梨夏 昨天 18:00
 
Golang相关测试框架

  在 Go 语言里,最常用的测试框架有:自带的 testing 包GoConveytestify
1、Go自带的testing包

   testing 包是官方内置的,无需额外安装,也是大多数项目的首选。
  (1)单元测试


  • 测试文件必须以 _test.go 结尾,例如:calc_test.go
  • 测试函数必须以 Test 开头(推荐写成 Test+函数名,更直观)
  • 测试函数签名固定:func TestXxx(t *testing.T),不能有返回值
  • 测试函数内部用 t.Errorf / t.Fatal 等方法输出错误

    • t.Errorf:标记测试失败,但 继续执行后面的测试代码
    • t.Fatal:标记测试失败,并 立即中止当前测试函数

  1. # 执行当前目录下所有 *_test.go 文件
  2. go test -v
  3. # 只执行指定文件中的测试
  4. go test -v calc_test.go calc.go
  5. # 执行指定的测试函数(-count=1 表示禁用缓存,强制重新运行)
  6. go test -v -run TestAdd calc_test.go calc.go -count=1
复制代码
  更详细的参数请参考:Go test命令行参数  (2)性能测试


  • 文件命名:和单测一样,文件必须以 _test.go 结尾,这样 go test 才能识别。
  • 函数命名:基准测试函数要以 Benchmark 开头,且必须是导出函数,比如 BenchmarkAdd。
  • 函数签名:基准测试函数必须接收一个 *testing.B 类型的参数,不能有返回值。
  • 重置计时器:b.ResetTimer() 用来清空前面初始化代码的耗时,保证只统计真正的测试部分。
  • 循环执行:基准测试里要用 for i := 0; i < b.N; i++ { ... },被测的代码放在循环里。
  • b.N 的作用:Go 框架会自动调整 b.N,让测试至少运行 1 秒左右,这样结果更稳定。最终输出的是平均每次运行的耗时(比如 0.25 ns/op 表示每次 0.25 纳秒)。
  在calc文件夹的calc_test.go文件中新增BenchmarkAbs和BenchmarkAdd方法。
1.png

   基准测试的目标就是 尽可能准确地衡量一段代码的性能,包括运行时间、内存分配等。

  • 核心思路:同一段代码运行很多次(b.N 次),用「总耗时 ÷ b.N」算出平均每次执行的耗时。
  • b.N 的作用:b.N 由 Go 测试框架自动决定,会动态调整,保证测试至少运行 1 秒左右,避免样本太少导致结果不准。
  • 运行过程:当执行 go test -bench=. 时,框架会先用很小的 b.N(比如 1、2、5、19)试跑几次,估算耗时,再逐步增大 b.N,直到结果稳定。
  • 输出示例:比如报告里写 1000000000 0.25 ns/op,意思是运行了 10 亿次,每次平均耗时 0.25 纳秒。
2.png

 
3.png

 
  1. # 运行所有基准测试
  2. go test -bench=.
  3. # 运行所有基准测试并显示内存分配情况
  4. go test -bench=. -benchmem
  5. # 只运行基准测试,不运行单元测试
  6. go test -run=none -bench=.
  7. # 指定测试时间
  8. go test -bench=. -benchtime=3s
  9. # 指定运行轮数
  10. go test -bench=. -count=3
  11. # 组合使用多种参数
  12. go test -run=none -bench=. -benchmem -benchtime=3s -count=3
复制代码
2、GoConvey

https://github.com/smartystreets/goconvey  GoConvey 是一款专门为 Golang 打造的测试框架,它不仅能帮你更方便地组织和运行单元测试,还自带了丰富的断言方法,让测试代码更简洁直观。更酷的是,它还支持 Web 界面:只要在项目里写好测试,用 go test 命令就能照常运行;如果你想要更直观的体验,可以直接运行 goconvey,然后在浏览器里访问 http://localhost:8080,就能看到实时的测试结果展示。  相比起纯粹使用标准库的 testing 包,Convey 让单元测试的书写和阅读都更流畅,尤其适合需要频繁跑测试、调试逻辑的开发过程。
  (1)安装依赖:go get github.com/smartystreets/goconvey

  (2)demo

4.png

 
5.png

 
3、testify

  Testify 是 Go 语言生态里非常常用的一个 断言风格测试框架。它不仅提供了开发者最常用的断言方法(让测试代码更简洁、可读性更强),还额外封装了三个核心功能模块:

  • Assertions(断言):内置了丰富的断言方法,避免重复编写样板式判断逻辑。
  • Suite(测试套件):支持将一组相关测试组织在一起,方便共享初始化、清理等逻辑。
  • Mock(模拟):提供强大的 Mock 能力,方便在单元测试中模拟依赖对象或外部接口。
  借助 Testify,我们可以用更接近自然语言的方式编写测试,不仅提高开发效率,也让测试结果更易于理解。
官方文档:https://godoc.org/github.com/stretchr/testify  (1)依赖安装:go get -u -v github.com/stretchr/testify

  (2)测试用例

  ① assert包提供了断言工具,在执行时会将case标记为失败,但程序不会退出,而是继续往下执行。
  1. package testify
  2. import (
  3.     "testing"
  4.     "github.com/stretchr/testify/assert"
  5. )
  6. func Test_assert(t *testing.T) {
  7.     a := 2
  8.     b := 3
  9.     // 第一个断言失败(故意写错期望值)
  10.     assert.Equal(t, 6, a+b, "第一个断言:2+3应该等于5,不是6")
  11.     // 程序继续执行下面的断言
  12.     t.Log("这行代码被执行了,说明程序没有中断")
  13.     // 第二个断言通过
  14.     assert.Equal(t, 5, a+b, "第二个断言:2+3应该等于5")
  15.     // 第三个断言也会执行
  16.     assert.True(t, a < b, "第三个断言:2应该小于3")
  17.     t.Log("所有断言都已执行完毕")
  18. }
复制代码
6.png

   ② require包提供与asser包相同的全局函数,与assert不同的是,require会终止当前测试。
  1. package testify
  2. import (
  3.     "testing"
  4.     "github.com/stretchr/testify/require"
  5. )
  6. func Test_require(t *testing.T) {
  7.     name := "Tom"
  8.     age := 18
  9.     // 这个会失败并停止测试
  10.     require.Equal(t, "dazuo", name, "测试会在这里停止")
  11.     require.Equal(t, 18, age, "测试会在这里停止")
  12.     // 这行不会被执行
  13.     t.Log("这行代码不会被执行")
  14. }
复制代码
7.png

   ③ mock包提供了一种轻松编写模拟对象的机制,可以在编写测试代码时替代实际对象使用模拟对象。
  1. func Test_mock(t *testing.T) {
  2.     // 创建模拟对象
  3.     mockS := &mockStorage{}
  4.     // 设置对Store方法期望的调用,参数必须匹配;返回该方法调用时的返回值
  5.     mockS.On("Store", "name", "Tom").Return(1, nil).Once()
  6.     result, err := mockS.Store("name", "Tom")
  7.     assert.NoError(t, err)
  8.     assert.Equal(t, 1, result)
  9.     mockS.AssertExpectations(t) // 断言所有预期的调用都发生了
  10. }
复制代码
8.png

   若该方法没有被调用:
  1. func Test_mock(t *testing.T) {
  2.     // 创建模拟对象
  3.     mockS := &mockStorage{}
  4.     // 设置对Store方法期望的调用,参数必须匹配;返回该方法调用时的返回值
  5.     mockS.On("Store", "name", "Tom").Return(1, nil).Once()
  6.     mockS.AssertExpectations(t) // 断言所有预期的调用都发生了
  7. }
复制代码
9.png

 
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册