零代码上手测试:Go语言内置测试框架介绍

简介: 零代码上手测试:Go语言内置测试框架介绍

/ Go 语言 Test 测试函数详解 /

 

一、概述

测试是保证代码质量非常重要的一环。Go 语言内置了测试功能,可以轻松编写单元测试。本文将详细介绍 Go 语言 testing 测试功能中的 Test 测试函数,包括使用方式、测试组织、benchmark 性能测试、示例代码等。

主要内容:

  • Test 函数基本用法
  • 子测试与 Setup/Teardown
  • 测试组织结构
  • 测试 cover 率
  • 基准测试 benchmark
  • 测试主函数
  • 测试框架集成
  • 并发测试
  • 示例 Demo

通过本文可以全面了解 Go 语言 Test 测试函数,并能应用到实际项目中去提高代码质量。


 

二、Test 函数基本用法

Go 语言中单元测试文件以 _test.go 命名,测试方法名以 Test 开头:

func TestAdd(t *testing.T) {
  total := Add(1, 2)
  if total != 3 {
    t.Errorf("Add(1, 2) failed, got %d", total) 
  }
}

测试函数 TestAdd 中,使用 t.Errorf 来报告错误信息。

testing.T 实际上是一个接口,代表一个测试对象,不同的测试框架都可以实现这个接口。我们通过它来记录测试日志。

常用的方法有:

  • t.Error/t.Errorf: 记录错误
  • t.Fatal/t.Fatalf: 记录错误并退出
  • t.Log/t.Logf: 记录日志
  • t.Parallel: 并发测试

Go 语言内置的 testing 框架实现了 testing.T 接口。


 

三、子测试与 Setup/Teardown

我们可以通过 Run 方法执行子测试,还可以通过 Setup/Teardown 执行初始化和清理:

func TestMulti(t *testing.T) {
  // 初始化代码
  t.Setup(func() {
    // ..
  })
  t.Run("A=1", func(t *testing.T) {
    t.Log("A=1")
  })
  t.Run("A=2", func(t *testing.T) {
    t.Log("A=2") 
  })
  // 清理代码
  t.Teardown(func() {
    // ..
  })
}

这样可以非常方便地对测试进行分组。


 

四、测试组织结构

复杂的测试我们可以通过以下结构组织:

  • testMain:全局测试初始化和 ErrorHandler
  • TestSuite:测试套件初始化,全局资源
  • TestCase:一个测试用例,一组相关的测试
  • Test:单个测试函数
  • subTest:子测试用例

这种层次清晰的测试组织结构,可以让我们完整体系地编写测试代码。


 

五、测试覆盖率

测试覆盖率(cover rage)可以度量测试的覆盖面。

启用 coverrage 实际上是通过注入计数器来实现。以示例:

func TestA(t *testing.T) {
  // 开启coverrage
  c := cover.NewCounter("testfile")
  c.Enable()
  // 测试代码
  ...
  // 查看测试覆盖率
  cover.Percent(c)
}

执行后可以得到运行的测试覆盖了百分之多少的代码。

这可以帮助我们分析还需要增加哪些测试。


 

六、基准测试

基准测试 Benchmark 可以测试函数/代码片段的性能。基准测试以 Benchmark 为前缀:

func BenchmarkAdd(b *testing.B) {
  for i := 0; i < b.N; i++ {
    Add(1, 2)
  }
}

运行基准测试,可以得到代码执行的时间。这样可以比较不同实现的性能。

我们应该针对关键路径进行基准测试,找到并优化速度慢的代码。


 

七、测试主函数

每份测试代码需要一个 TestMain 函数作为入口:

func TestMain(m *testing.M) {
  // 初始化代码
  // 执行测试
  code := m.Run() 
  // 释放资源
  ...
  os.Exit(code)
}

TestMain 会先执行初始化操作,然后使用 m.Run 执行测试代码,最后释放资源并退出程序。/


 

八、测试框架集成

Go 语言内置 testing 框架非常简单。我们也可以集成其他测试框架。

常见的集成方式是实现 testing.TB 接口。以集成 Ginkgo 为例:

type TestWrapper struct {
  *testing.T
}
func (t *TestWrapper) Fail() {
  t.FailNow()
}
var RunSpecs = func(t *testing.T) {
  RegisterFailHandler(Fail)
  var tr TestReporter
  RunSpecsWithDefaultAndCustomReporters(t, "SuiteName", []Reporter{tr})
}
func TestSuite(t *testing.T) {
  RunSpecs(t)
}

这样我们就可以在 Go 测试中无缝使用 Ginkgo 的 BDD 语法了。


 

九、并发测试

Go 语言支持并发执行测试来提高效率:

func TestParallel(t *testing.T) {
  for _, fn := range tests {
    t.Run(fn.Name(), func(t *testing.T) {
      t.Parallel() // 并发执行测试函数
      fn(t)
    })
  }
}

通过 t.Parallel()可以启用并发测试。

需要注意并发访问全局资源的锁保护以避免冲突。


 

十、完整示例

我们来看一个完整的测试示例:

// 测试对象
type Foo struct {} 
// 测试函数
func TestFoo(t *testing.T) {
  // 测试套件初始化
  t.SetupTestSuite(func() {
    // ..
  })
  t.Run("A=1", func(t *testing.T) {
    t.Parallel() // 并发
    // 测试前初始化 
    t.Setup(func() {
      // ..
    })
    // 调用测试对象  
    f := Foo{}
    // 测试逻辑  
    ...
    // 测试后清理
    t.Teardown(func() {
      // .. 
    })
  })
  // 基准测试
  t.Benchmark("BM", func(b *testing.B) {
    for i := 0; i < b.N; i++ {
      // ..
    }
  })
  // 测试套件结束处理
  t.TeardownTestSuite(func() {
    // ..
  })  
}
func TestMain(m *testing.M) {
  // 初始化
  os.Exit(m.Run())
}

这样一个完整的测试组织结构,可以让我们的测试代码看起来非常清晰系统。


 

十一、总结

通过本文的介绍,我们已经可以熟练使用 Go 语言中的测试功能,编写规范的单元测试了。

testing 提供了强大灵活的测试支持,可以编写出高质量可靠的测试代码。这对于提高我们项目的健壮性非常重要。

在后续的 Go 项目中,希望大家积极编写 Unit Test,让代码质量达到更高的层次。


目录
相关文章
|
6天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
4天前
|
设计模式 前端开发 JavaScript
自动化测试框架设计原则与最佳实践####
本文深入探讨了构建高效、可维护的自动化测试框架的核心原则与策略,旨在为软件测试工程师提供一套系统性的方法指南。通过分析常见误区,结合行业案例,阐述了如何根据项目特性定制自动化策略,优化测试流程,提升测试覆盖率与执行效率。 ####
21 6
|
4天前
|
人工智能 前端开发 测试技术
探索软件测试中的自动化框架选择与优化策略####
本文深入剖析了当前主流的自动化测试框架,通过对比分析各自的优势、局限性及适用场景,为读者提供了一套系统性的选择与优化指南。文章首先概述了自动化测试的重要性及其在软件开发生命周期中的位置,接着逐一探讨了Selenium、Appium、Cypress等热门框架的特点,并通过实际案例展示了如何根据项目需求灵活选用与配置框架,以提升测试效率和质量。最后,文章还分享了若干最佳实践和未来趋势预测,旨在帮助测试工程师更好地应对复杂多变的测试环境。 ####
18 4
|
4天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
13 2
|
4天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
15 2
|
7天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
4天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
12 4
|
4天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
17 1
|
8天前
|
Go 数据处理 调度
探索Go语言的并发模型:Goroutines与Channels的协同工作
在现代编程语言中,Go语言以其独特的并发模型脱颖而出。本文将深入探讨Go语言中的Goroutines和Channels,这两种机制如何协同工作以实现高效的并发处理。我们将通过实际代码示例,展示如何在Go程序中创建和管理Goroutines,以及如何使用Channels进行Goroutines之间的通信。此外,本文还将讨论在使用这些并发工具时可能遇到的常见问题及其解决方案,旨在为Go语言开发者提供一个全面的并发编程指南。
|
6天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。