刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • go语言基础

  • go语言进阶

  • go语言实现原理

  • gin框架

  • gorm

  • go测试

    • 单元测试
      • go test工具
      • 单元测试函数
        • 1. 格式
        • 2. 单元测试示例
      • go test -v
      • go test -run
      • 回归测试
      • 跳过某些测试用例
      • 子测试
      • 表格驱动测试
        • 1. 介绍
        • 2. 示例
        • 3. 并行测试
        • 4. 使用工具生成测试代码
      • 测试覆盖率
    • benchmark基准测试
    • pprof 性能分析
  • Go语言
  • go测试
bigox
2022-06-28
目录

单元测试

# go test工具

  • go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。

  • 在*_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。

    类型 格式 作用
    测试函数 函数名前缀为Test 测试程序的一些逻辑行为是否正确
    基准函数 函数名前缀为Benchmark 测试函数的性能
    示例函数 函数名前缀为Example 为文档提供示例文档
  • go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。

# 单元测试函数

# 1. 格式

  • 每个测试函数必须导入testing包,测试函数的基本格式(签名)如下:

    func TestName(t *testing.T){
        // ...
    }
    
    1
    2
    3
  • 测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头,举几个例子:

    func TestAdd(t *testing.T){ ... }
    func TestSum(t *testing.T){ ... }
    func TestLog(t *testing.T){ ... }
    
    1
    2
    3
  • 其中参数t用于报告测试失败和附加的日志信息。 testing.T的拥有的方法如下:

    func (c *T) Cleanup(func())
    func (c *T) Error(args ...interface{})
    func (c *T) Errorf(format string, args ...interface{})
    func (c *T) Fail()
    func (c *T) FailNow()
    func (c *T) Failed() bool
    func (c *T) Fatal(args ...interface{})
    func (c *T) Fatalf(format string, args ...interface{})
    func (c *T) Helper()
    func (c *T) Log(args ...interface{})
    func (c *T) Logf(format string, args ...interface{})
    func (c *T) Name() string
    func (c *T) Skip(args ...interface{})
    func (c *T) SkipNow()
    func (c *T) Skipf(format string, args ...interface{})
    func (c *T) Skipped() bool
    func (c *T) TempDir() string
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 2. 单元测试示例

  • 就像细胞是构成我们身体的基本单位,一个软件程序也是由很多单元组件构成的。单元组件可以是函数、结构体、方法和最终用户可能依赖的任意东西。总之我们需要确保这些组件是能够正常运行的。单元测试是一些利用各种方法测试单元组件的程序,它会将结果与预期输出进行比较。

  • 接下来,我们在base_demo包中定义了一个Split函数,具体实现如下:

    // base_demo/split.go
    
    package base_demo
    
    import "strings"
    
    // Split 把字符串s按照给定的分隔符sep进行分割返回字符串切片
    func Split(s, sep string) (result []string) {
      i := strings.Index(s, sep)
    
      for i > -1 {
        result = append(result, s[:i])
        s = s[i+1:]
        i = strings.Index(s, sep)
      }
      result = append(result, s)
      return
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • 在当前目录下,我们创建一个split_test.go的测试文件,并定义一个测试函数如下:

    // split/split_test.go
    
    package split
    
    import (
      "reflect"
      "testing"
    )
    
    func TestSplit(t *testing.T) { // 测试函数名必须以Test开头,必须接收一个*testing.T类型参数
      got := Split("a:b:c", ":")         // 程序输出的结果
      want := []string{"a", "b", "c"}    // 期望的结果
      if !reflect.DeepEqual(want, got) { // 因为slice不能比较直接,借助反射包中的方法比较
        t.Errorf("expected:%v, got:%v", want, got) // 测试失败输出错误提示
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  • 此时split这个包中的文件如下:

    ❯ ls -l
    total 16
    -rw-r--r--  1   staff  408  4 29 15:50 split.go
    -rw-r--r--  1   staff  466  4 29 16:04 split_test.go
    
    1
    2
    3
    4
  • 在当前路径下执行go test命令,可以看到输出结果如下:

    ❯ go test
    PASS
    ok      golang-unit-test-demo/base_demo       0.005s
    
    1
    2
    3

# go test -v

  • 一个测试用例有点单薄,我们再编写一个测试使用多个字符切割字符串的例子,在split_test.go中添加如下测试函数:

    func TestSplitWithComplexSep(t *testing.T) {
      got := Split("abcd", "bc")
      want := []string{"a", "d"}
      if !reflect.DeepEqual(want, got) {
        t.Errorf("expected:%v, got:%v", want, got)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
  • 现在我们有多个测试用例了,为了能更好的在输出结果中看到每个测试用例的执行情况,我们可以为go test命令添加-v参数,让它输出完整的测试结果。

    ❯ go test -v
    === RUN   TestSplit
    --- PASS: TestSplit (0.00s)
    === RUN   TestSplitWithComplexSep
        split_test.go:20: expected:[a d], got:[a cd]
    --- FAIL: TestSplitWithComplexSep (0.00s)
    FAIL
    exit status 1
    FAIL    golang-unit-test-demo/base_demo 0.009s
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 从上面的输出结果我们能清楚的看到是TestSplitWithComplexSep这个测试用例没有测试通过。

# go test -run

  • 单元测试的结果表明split函数的实现并不可靠,没有考虑到传入的sep参数是多个字符的情况,下面我们来修复下这个Bug:

    package base_demo
    
    import "strings"
    
    // Split 把字符串s按照给定的分隔符sep进行分割返回字符串切片
    func Split(s, sep string) (result []string) {
      i := strings.Index(s, sep)
    
      for i > -1 {
        result = append(result, s[:i])
        s = s[i+len(sep):] // 这里使用len(sep)获取sep的长度
        i = strings.Index(s, sep)
      }
      result = append(result, s)
      return
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  • 在执行go test命令的时候可以添加-run参数,它对应一个正则表达式,只有函数名匹配上的测试函数才会被go test命令执行。

  • 例如通过给go test添加-run=Sep参数来告诉它本次测试只运行TestSplitWithComplexSep这个测试用例:

    ❯ go test -run=Sep -v
    === RUN   TestSplitWithComplexSep
    --- PASS: TestSplitWithComplexSep (0.00s)
    PASS
    ok      golang-unit-test-demo/base_demo 0.010s
    
    1
    2
    3
    4
    5
  • 最终的测试结果表情我们成功修复了之前的Bug。

# 回归测试

  • 修改了代码之后仅仅执行那些失败的测试用例或新引入的测试用例是错误且危险的,正确的做法应该是完整运行所有的测试用例,保证不会因为修改代码而引入新的问题。

    ❯ go test -v
    === RUN   TestSplit
    --- PASS: TestSplit (0.00s)
    === RUN   TestSplitWithComplexSep
    --- PASS: TestSplitWithComplexSep (0.00s)
    PASS
    ok      golang-unit-test-demo/base_demo 0.011s
    
    1
    2
    3
    4
    5
    6
    7
  • 测试结果表明我们的单元测试全部通过。

  • 通过这个示例我们可以看到,有了单元测试就能够在代码改动后快速进行回归测试,极大地提高开发效率并保证代码的质量。

# 跳过某些测试用例

  • 为了节省时间支持在单元测试时跳过某些耗时的测试用例。

    func TestTimeConsuming(t *testing.T) {
        if testing.Short() {
            t.Skip("short模式下会跳过该测试用例")
        }
        ...
    }
    
    1
    2
    3
    4
    5
    6
  • 当执行go test -short时就不会执行上面的TestTimeConsuming测试用例。

# 子测试

  • 在上面的示例中我们为每一个测试数据编写了一个测试函数,而通常单元测试中需要多组测试数据保证测试的效果。Go1.7+中新增了子测试,支持在测试函数中使用t.Run执行一组测试用例,这样就不需要为不同的测试数据定义多个测试函数了。

    func TestXXX(t *testing.T){
      t.Run("case1", func(t *testing.T){...})
      t.Run("case2", func(t *testing.T){...})
      t.Run("case3", func(t *testing.T){...})
    }
    
    1
    2
    3
    4
    5

# 表格驱动测试

# 1. 介绍

  • 表格驱动测试不是工具、包或其他任何东西,它只是编写更清晰测试的一种方式和视角。

  • 编写好的测试并非易事,但在许多情况下,表格驱动测试可以涵盖很多方面:表格里的每一个条目都是一个完整的测试用例,包含输入和预期结果,有时还包含测试名称等附加信息,以使测试输出易于阅读。

  • 使用表格驱动测试能够很方便的维护多个测试用例,避免在编写单元测试时频繁的复制粘贴。

  • 表格驱动测试的步骤通常是定义一个测试用例表格,然后遍历表格,并使用t.Run对每个条目执行必要的测试。

# 2. 示例

  • 官方标准库中有很多表格驱动测试的示例,例如fmt包中便有如下测试代码:

    var flagtests = []struct {
      in  string
      out string
    }{
      {"%a", "[%a]"},
      {"%-a", "[%-a]"},
      {"%+a", "[%+a]"},
      {"%#a", "[%#a]"},
      {"% a", "[% a]"},
      {"%0a", "[%0a]"},
      {"%1.2a", "[%1.2a]"},
      {"%-1.2a", "[%-1.2a]"},
      {"%+1.2a", "[%+1.2a]"},
      {"%-+1.2a", "[%+-1.2a]"},
      {"%-+1.2abc", "[%+-1.2a]bc"},
      {"%-1.2abc", "[%-1.2a]bc"},
    }
    func TestFlagParser(t *testing.T) {
      var flagprinter flagPrinter
      for _, tt := range flagtests {
        t.Run(tt.in, func(t *testing.T) {
          s := Sprintf(tt.in, &flagprinter)
          if s != tt.out {
            t.Errorf("got %q, want %q", s, tt.out)
          }
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  • 通常表格是匿名结构体切片,可以定义结构体或使用已经存在的结构进行结构体数组声明。name属性用来描述特定的测试用例。

  • 接下来让我们试着自己编写表格驱动测试:

    func TestSplitAll(t *testing.T) {
      // 定义测试表格
      // 这里使用匿名结构体定义了若干个测试用例
      // 并且为每个测试用例设置了一个名称
      tests := []struct {
        name  string
        input string
        sep   string
        want  []string
      }{
        {"base case", "a:b:c", ":", []string{"a", "b", "c"}},
        {"wrong sep", "a:b:c", ",", []string{"a:b:c"}},
        {"more sep", "abcd", "bc", []string{"a", "d"}},
        {"leading sep", "沙河有沙又有河", "沙", []string{"", "河有", "又有河"}},
      }
      // 遍历测试用例
      for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) { // 使用t.Run()执行子测试
          got := Split(tt.input, tt.sep)
          if !reflect.DeepEqual(got, tt.want) {
            t.Errorf("expected:%#v, got:%#v", tt.want, got)
          }
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  • 在终端执行go test -v,会得到如下测试输出结果:

    ❯ go test -v
    === RUN   TestSplit
    --- PASS: TestSplit (0.00s)
    === RUN   TestSplitWithComplexSep
    --- PASS: TestSplitWithComplexSep (0.00s)
    === RUN   TestSplitAll
    === RUN   TestSplitAll/base_case
    === RUN   TestSplitAll/wrong_sep
    === RUN   TestSplitAll/more_sep
    === RUN   TestSplitAll/leading_sep
    --- PASS: TestSplitAll (0.00s)
        --- PASS: TestSplitAll/base_case (0.00s)
        --- PASS: TestSplitAll/wrong_sep (0.00s)
        --- PASS: TestSplitAll/more_sep (0.00s)
        --- PASS: TestSplitAll/leading_sep (0.00s)
    PASS
    ok      golang-unit-test-demo/base_demo 0.010s
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 3. 并行测试

  • 表格驱动测试中通常会定义比较多的测试用例,而Go语言又天生支持并发,所以很容易发挥自身并发优势将表格驱动测试并行化。 想要在单元测试过程中使用并行测试,可以像下面的代码示例中那样通过添加t.Parallel()来实现。

    func TestSplitAll(t *testing.T) {
      t.Parallel()  // 将 TLog 标记为能够与其他测试并行运行
      // 定义测试表格
      // 这里使用匿名结构体定义了若干个测试用例
      // 并且为每个测试用例设置了一个名称
      tests := []struct {
        name  string
        input string
        sep   string
        want  []string
      }{
        {"base case", "a:b:c", ":", []string{"a", "b", "c"}},
        {"wrong sep", "a:b:c", ",", []string{"a:b:c"}},
        {"more sep", "abcd", "bc", []string{"a", "d"}},
        {"leading sep", "沙河有沙又有河", "沙", []string{"", "河有", "又有河"}},
      }
      // 遍历测试用例
      for _, tt := range tests {
        tt := tt  // 注意这里重新声明tt变量(避免多个goroutine中使用了相同的变量)
        t.Run(tt.name, func(t *testing.T) { // 使用t.Run()执行子测试
          t.Parallel()  // 将每个测试用例标记为能够彼此并行运行
          got := Split(tt.input, tt.sep)
          if !reflect.DeepEqual(got, tt.want) {
            t.Errorf("expected:%#v, got:%#v", tt.want, got)
          }
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  • 这样我们执行go test -v的时候就会看到每个测试用例并不是按照我们定义的顺序执行,而是互相并行了。

# 4. 使用工具生成测试代码

  • 社区里有很多自动生成表格驱动测试函数的工具,比如gotests (opens new window)等,很多编辑器如Goland也支持快速生成测试文件。

  • 安装

    go get -u github.com/cweill/gotests/...
    
    1
  • 执行

    gotests -all -w split.go
    
    1
  • 上面的命令表示,为split.go文件的所有函数生成测试代码至split_test.go文件(目录下如果事先存在这个文件就不再生成)。

  • 生成的测试代码大致如下:

    package base_demo
    
    import (
      "reflect"
      "testing"
    )
    
    func TestSplit(t *testing.T) {
      type args struct {
        s   string
        sep string
      }
      tests := []struct {
        name       string
        args       args
        wantResult []string
      }{
        // TODO: Add test cases.
      }
      for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
          if gotResult := Split(tt.args.s, tt.args.sep); !reflect.DeepEqual(gotResult, tt.wantResult) {
            t.Errorf("Split() = %v, want %v", gotResult, tt.wantResult)
          }
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  • 代码格式与我们上面的类似,只需要在TODO位置添加我们的测试逻辑就可以了。

# 测试覆盖率

  • 测试覆盖率是指代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率,也就是在测试中至少被运行一次的代码占总代码的比例。在公司内部一般会要求测试覆盖率达到80%左右。

  • Go提供内置功能来检查你的代码覆盖率,即使用go test -cover来查看测试覆盖率。

    ❯ go test -cover
    PASS
    coverage: 100.0% of statements
    ok      golang-unit-test-demo/base_demo 0.009s
    
    1
    2
    3
    4
  • 从上面的结果可以看到我们的测试用例覆盖了100%的代码。

  • Go还提供了一个额外的-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件。例如:

    ❯ go test -cover -coverprofile=c.out
    PASS
    coverage: 100.0% of statements
    ok      golang-unit-test-demo/base_demo 0.009s
    
    1
    2
    3
    4
  • 上面的命令会将覆盖率相关的信息输出到当前文件夹下面的c.out文件中。

    ❯ tree .
    .
    ├── c.out
    ├── split.go
    └── split_test.go
    
    1
    2
    3
    4
    5
  • 然后我们执行go tool cover -html=c.out,使用cover工具来处理生成的记录信息,该命令会打开本地的浏览器窗口生成一个HTML报告。

    image-20220628165356943

    • 上图中每个用绿色标记的语句块表示被覆盖了,而红色的表示没有被覆盖。
#Go#
上次更新: 2023/04/16, 18:35:33
GORM_CURD操作指南
benchmark基准测试

← GORM_CURD操作指南 benchmark基准测试→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式