Go 语言进阶单元测试示例详解

目录
  • 前言
  • 测试
  • 单元测试
    • 规则
    • 示例
    • assert
    • 覆盖率
    • 依赖
  • Mock
  • 基准测试

前言

本文从单元测试实践角度出发,提升对代码质量的意识。

本文内容主要包括:单元测试、Mock测试、基准测试。

测试

测试可以提高代码的质量、减少事故的发生。

测试又分为:回归测试、集成测试、单元测试。

回归测试是指对QA手动回归一些特定场景,可以理解为我们说的手动点点。

集成测试是指对系统功能维度做验证,比如对服务暴露的接口验证,一般是自动化的验证。

单元测试是指在开发阶段,开发者对单独的函数、模块做验证,写一些测试用例。

单元测试

单元测试组成部分:输入、输出、测试单元、与期望的校对,测试单元又包括函数、接口、模块、复杂的聚合函数等。

通过单元测试的输出再与期望输出进行校对,来验证代码的正确性。通过单元测试可以保证代码的质量,也可以在一定程度上提升效率,比如通过运行单元测试可以快速定位到有问题的代码。

规则

单元测试的编写有一定的规则:

  • 所有测试文件以 _test.go 结尾
  • 测试方法名以Test开头,参数要用testing func TestXxx(t *testing.T)
  • 测试初始化逻辑放到TestMain
  • 通过go test命令进行测试

示例

import "testing"
func TestHelloTom(t *testing.T) {
    output := HelloTom()
    expectOutPut := "Tom"
    if output != expectOutPut {
        t.Errorf("Expected %s do not match actual %s", expectOutPut, output)
    }
}
func HelloTom() string {
    return "Jerry"
}

通过go test命令运行得到以下结果,从测试结果里可以看出,我们期望得到的是Tom,但实际得到的却是Jerry

--- FAIL: TestHelloTom (0.00s)
helloTom_test.go:9: Expected Tom do not match actual Jerry
FAIL
exit status 1
FAIL    learning/mytesting      0.496s

assert

另外我们可以使用开源的assert包,来代替我们自己的if判断。

func TestHelloTom(t *testing.T) {
    output := HelloTom()
    expectOutPut := "Tom"
    assert.Equal(t, expectOutPut, output)
}

再次通过go test命令运行得到以下结果,输出了更详细的堆栈信息。

覆盖率

如何评估单元测试呢?是通过代码覆盖率来评估的,评估的标准包括:

  • 衡量代码是否经过了足够的测试
  • 评价项目的测试水准
  • 评估项目是否达到了高水平的测试等级

下面通过一个例子来看下代码覆盖率:

// judgepass.go
func JudgePassLine(score int16) bool {
    if score >= 16 {
        return true
    }
    return false
}
// judgepass_test.go
func TestJudgePassLineTrue(t *testing.T) {
    isPass := JudgePassLine(70)
    assert.Equal(t, true, isPass)
}

通过命令来看覆盖率go test judgepass_test.go judgepass.go --cover

输出结果为:

ok  command-line-arguments  0.327s  coverage: 66.7% of statements

可以看到,提示出的代码覆盖率为66.7%,因为只走了一个if分支。如果要想达到100%的代码覆盖率的话,就要把所有的分支都要覆盖到。

一般的覆盖率为50%~60%,较高的覆盖率要达到80%+。要注意:测试分支相互独立、要全面覆盖,测试单元粒度要足够小,满足函数单一职责。

依赖

一个实际项目不可能只是一个简单的单体函数,肯定会很复杂,存在其他的依赖,比如依赖数据库、redis、文件等外部依赖。

单元测试一般有两个目标:幂等、稳定。

幂等:重复执行一个用例、调用一个接口,返回的结果是一样的。

稳定:单元测试是相互隔离的,在任何时间都能独立运行。

Mock

如果单元测试用到数据库、redis等,在单元测试里直接连接会涉及到网络传输,这是不稳定的,所以要用到Mock机制。

开源Mock框架:github.com/bouk/monkey

这个Mock包可以对函数或方法进行打桩,打桩就是用一个函数A来替换一个函数B。

monkey的实现原理主要是在运行时,通过Gounsafe包能够将内存中函数的地址替换为运行时函数的地址,最终调用的是打桩函数,从而实现Mock的功能。

Mock常用方法:PatchUnpatch

Patch方法有两个参数,target为替换的函数(原函数),replacement为要替换成的函数。

func Patch(target, replacement interface{}) *PatchGuard {
    t := reflect.ValueOf(target)
    r := reflect.ValueOf(replacement)
    patchValue(t, r)
    return &PatchGuard{t, r}
}

Unpatch为测试结束之后,要把打的桩给卸载掉。

func Unpatch(target interface{}) bool {
    return unpatchValue(reflect.ValueOf(target))
}

下面通过Mock来模拟对文件的操作。

func TestProcessFirstLineWithMock(t *testing.T) {
    monkey.Patch(ReadFirstLine, func() string {
        return "line110"
    })
    defer monkey.Unpatch(ReadFirstLine)
    line := ProcessFirstLine()
    assert.Equal(t, "line000", line)
}
func ReadFirstLine() string {
    open, err := os.Open("log")
    defer open.Close()
    if err != nil {
        return ""
    }
    scanner := bufio.NewScanner(open)
    for scanner.Scan() {
        return scanner.Text()
    }
    return ""
}
func ProcessFirstLine() string {
    line := ReadFirstLine()
    destLine := strings.ReplaceAll(line, "11", "00")
    return destLine
}

该测试用例对ProcessFirstLine函数进行测试,这个函数调用了ReadFirstLine函数,涉及到文件的操作,通过Mock对文件的操作进行打桩,这样就避免了其他进程对文件操作的影响。

基准测试

Go还提供了基准测试框架,可以测试一段程序的性能、CPU消耗,可以对代码做性能分析,测试方法与单元测试类似。

基准测试规则:

  • 基准测试以Benchmark为前缀
  • 需要一个*testing.B类型的参数b
  • 基准测试必须要执行b.N次

下面通过一个模拟负载均衡的例子,来看下基准测试:

var ServerIndex [10]int
func InitServerIndex() {
    for i := 0; i < 10; i++ {
        ServerIndex[i] = i + 100
    }
}
func Select() int {
    return ServerIndex[rand.Intn(10)]
}
func BenchmarkSelect(b *testing.B) {
    InitServerIndex()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        Select()
    }
}
func BenchmarkSelectParallel(b *testing.B)  {
    InitServerIndex()
    b.ResetTimer()
    b.RunParallel(func (pb *testing.PB)  {
        for pb.Next() {
            Select()
        }
    })
}

通过命令 go test -bench=. 运行测试,输出结果如下:

goos: darwin
goarch: amd64
pkg: learning/bench
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.70GHz
BenchmarkSelect-8               50264580               23.47 ns/op
BenchmarkSelectParallel-8       13717840               133.4 ns/op
PASS
ok      learning/bench  4.559s

BenchmarkSelect-8 表示对Select函数进行基准测试,数字8表示 GOMAXPROCS 的值。

23.47 ns/op 表示每次调用Select函数耗时23.47ns

50264580 这是50264580次调用的平均值。

字节开源的go框架:github.com/bytedance/g…

引用 Go 语言进阶与依赖管理

以上就是Go 语言进阶单元测试示例详解的详细内容,更多关于Go 语言单元测试的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go1.18新特性工作区模糊测试及泛型的使用详解

    目录 前言 Go工作区模式(Go Workspace Mode) 现实的情况 多仓库同时开发 多个新仓库开始开发 工作区模式是什么 推荐的使用方法 使用时的注意点 Go模糊测试(Go Fuzzing Test) 为什么Golang要支持模糊测试 模糊测试是什么 Golang的模糊测试如何使用 最简单的实践例子 提供自定义语料 使用时的注意点 Go的泛型 类型参数(Type Parameters) 类型集合(Type Sets) 类型推导(Type Inference) 类型统一化(Type Un

  • Go单元测试对数据库CRUD进行Mock测试

    目录 go-sqlmock 安装 使用示例 miniredis 安装 使用示例 总结 最近在实践中也总结了一些如何用表格驱动的方式使用 gock Mock测试外部接口调用.以及怎么对GORM做mock测试,这些等这篇学完基础后,后面再单独写文章给大家介绍. 这是Go语言单元测试系列教程的第3篇,介绍了如何使用go-sqlmock和miniredis工具进行MySQL和Redis的mock测试. 在上一篇<Go单元测试--模拟服务请求和接口返回>中,我们介绍了如何使用httptest和gock工

  • Go语言测试库testify使用学习

    目录 简介 准备工作 assert Contains DirExists ElementsMatch Empty EqualError EqualValues Error ErrorAs ErrorIs 逆断言 Assertions 对象 require mock suite 测试 HTTP 服务器 总结 简介 testify可以说是最流行的(从 GitHub star 数来看)Go 语言测试库了.testify提供了很多方便的函数帮助我们做assert和错误信息输出.使用标准库testing,

  • 详解Go 语言如何通过测试保证质量

    目录 引言 单元测试 什么是单元测试 Go 语言的单元测试 单元测试覆盖率 基准测试 什么是基准测试 Go 语言的基准测试 计时方法 内存统计 并发基准测试 基准测试实战 总结 引言 本节带你学习本专栏的第四模块:工程管理.现在项目的开发都不是一个人可以完成的,需要多人进行协作,那么在多人协作中如何保证代码的质量,你写的代码如何被其他人使用,如何优化代码的性能等, 就是第四模块的内容. 这一讲首先来学习 Go 语言的单元测试和基准测试. 单元测试 在开发完一个功能后,你可能会直接把代码合并到代码

  • Go语言自带测试库testing使用教程

    目录 简介 单元测试 表格驱动测试 分组和并行 主测试函数 其他 性能测试 其他选项 示例测试 总结 简介 testing是 Go 语言标准库自带的测试库.在 Go 语言中编写测试很简单,只需要遵循 Go 测试的几个约定,与编写正常的 Go 代码没有什么区别.Go 语言中有 3 种类型的测试:单元测试,性能测试,示例测试.下面依次来介绍. 单元测试 单元测试又称为功能性测试,是为了测试函数.模块等代码的逻辑是否正确.接下来我们编写一个库,用于将表示罗马数字的字符串和整数互转.罗马数字是由M/D/

  • Go单元测试利器testify使用示例详解

    目录 testify assert 包 require 包 mock 包 suite 包 testify 在团队里推行单元测试的时候,有一个反对的意见是:写单元测试耗时太多.且不论这个意见对错,单元测试确实不应该太费时间.这时候,一个好的单测辅助工具,显得格外重要.本文推荐的 testify(github.com/stretchr/te…) 包,具有断言.mock 等功能,能配合标准库,使你的单元测试更加简洁易读. testify 有三个主要功能: 断言,在 assert 包和 require

  • Go语言工程实践单元测试基准测试示例详解

    目录 背景 测试 单元测试 演示 覆盖率 依赖 文件处理 Mock 基准测试 小结 背景 测试的出现是为了避免项目中出现重大事故 测试是避免事故的最后一道屏障 测试 单元测试的覆盖率在一定程度上而言,决定了代码的质量 单元测试 通过测试单元的输出与期望值进行校对从而验证代码的正确性,从而保证新旧代码的互不影响与程序的正常运行. 进而单元测试较于编译更易于在较短的周期内发现和定位代码中的错误使损失最小化从而提升效率.所以写单元测试是很有必要的. Golang单元测试对文件名和方法名,参数都有很严格

  • Golang 单元测试和基准测试实例详解

    目录 前言 Go 单元测试 单元测试覆盖率 基准测试 前言 多人协作的项目里,要保证代码的质量,自然离不开单元测试.开发完一个功能后肯定要对所写的代码进行测试,测试没有问题之后再合并到代码库供他人使用.如果强行合并到代码库可能会影响其他人开发,被上线的话肯定也会导致线上 Bug ,影响用户使用. 所以,单元测试也是一个很重要的事情.单元测试是指在开发中,对一个函数或模块的测试.其强调的是对单元进行测试. Go 单元测试 Go 语言提供了单元测试的框架,只要遵循其规则即可: 测试文件命名: 单元测

  • Go 语言进阶单元测试示例详解

    目录 前言 测试 单元测试 规则 示例 assert 覆盖率 依赖 Mock 基准测试 前言 本文从单元测试实践角度出发,提升对代码质量的意识. 本文内容主要包括:单元测试.Mock测试.基准测试. 测试 测试可以提高代码的质量.减少事故的发生. 测试又分为:回归测试.集成测试.单元测试. 回归测试是指对QA手动回归一些特定场景,可以理解为我们说的手动点点. 集成测试是指对系统功能维度做验证,比如对服务暴露的接口验证,一般是自动化的验证. 单元测试是指在开发阶段,开发者对单独的函数.模块做验证,

  • Go语言基础反射示例详解

    目录 概述 语法 一.基本操作 二.修改目标对象 三.动态调用方法 总结 示例 概述 在程序运行期对程序动态的进行访问和修改 reflect godoc: https://golang.org/pkg/reflect/ reflect包有两个数据类型: Type:数据类型 [reflect.TypeOf():是获取Type的方法] Value:值的类型[reflect.ValueOf():是获取Value的方法] 语法 一.基本操作 获取变量类型 func TypeOf(i interface{

  • Go语言字符串基础示例详解

    目录 包含 Contains(s,substr string) bool ContainsAny(s, chars string) bool 统计 Count(s, substr string) int 比较 EqualFold(s, t string) bool HasPrefix(s, prefix string) bool HasSuffix(s, suffix string) bool 分割 Fields(s string) []string 下标 Index(s, sep string

  • C语言指针教程示例详解

    目录 指针 内存 指针类型 指针运算 二级指针 指针数组 指针 指针提供了对地址操作的一种方法,因此,使用指针可使得 C 语言能够更高效地实现对计算机底层硬件的操作.另外,通过指针可以更便捷地操作数组.在一定意义上可以说,指针是 C 语言的精髓. 概念解释就不去搬原定义了,又臭又长不好理解,精炼两点就是: 1.指针是内存中的一个最小单元的编号,也就是地址: 2.平时我们说的指针,通常是指指针变量,用来存储内存地址的变量 也就是说:指针就是地址,口语中指针通常是指针变量 内存 要搞明白指针首先要搞

  • 关于Vue Webpack2单元测试示例详解

    前言 最近发现一个问题,vue-cli 提供的官方模板确实好用.但一直用下来手贱毛病又犯了,像穿了别人的内衣,总感觉不舒服. 所以有机会就瞎倒腾了一遍,总算把各个流程摸了一把.这里分享一下配置带覆盖率的单元测试.分享出来供大家参考学习,话不多少了,来一起看看详细的介绍: 一.文件结构 基本的文件结构. ├─src │ ├─assets │ ├─components │ ├─app.vue │ └─main.js ├─test │ └─unit │ ├─coverage │ ├─specs │ ├

  • Go语言基础单元测试与性能测试示例详解

    目录 概述 单元测试 代码说明如下 问题 注意 性能测试 基本使用 自定义测试时间 概述 测试不是Go语言独有的,其实在很多语言都有测试,例如:Go.Java.Python- 要想成为一名合格的大牛,这是程序员必须具备的一项技能,特别是一些大公司,这是加分的一项,主要有如下优点: 代码可以随时测试,保证代码不会产生错误 写出更加高效的代码 testing文档 Testing_flags文档 单元测试 格式:func TestXXX(t *testing.T) //add.go package c

  • C语言进阶栈帧示例详解教程

    目录 正片开始 栈有什么用? 寄存器 main函数创建 局部变量创建 函数部分 形参与实参 正片开始 今天来讲讲我对栈帧创建与销毁的拙见.理解什么是栈帧首先知道什么是栈: 在数据结构中, 栈是限定仅在表尾进行插入或删除操作的线性表.栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据. 栈有什么用? 在计算机系统中,栈也可以称之为栈内存是一个具有动态内存区域,存储函数内部(包括main函数)的局部变量和方法调用和函数参数值,

  • C语言实现单元测试的示例详解

    目录 前沿 使用前提 测试框架如下 测试方法编写文件 验证 前沿 单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等.总的来说,单元就是人为规定的最小的被测功能模块.单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试. 在网上找了找C语言都没有类似j

  • Swift进阶教程Mirror反射示例详解

    目录 元类型与.self AnyObject AnyClass Any type(Of:) self self在方法里面的作用 Self Swift Runtime Mirror Mirror的基本用法 Mirror的简单应用-JSON解析 Mirror源码解析 Enum Metadata探索 还原TargetEnumMetadata 还原TargetEnumDescriptor 相对偏移指针 打印枚举中的属性 Struct Metadata探索 获取结构体的属性 swift_getTypeBy

随机推荐