李二狗的星球

V1

2022/02/27阅读:52主题:默认主题

golang的一些测试技巧和工具

此前关于测试写过一篇内容一文搞定golang单元测试

今天这篇内容是对测试内容的补充,目录如下:

表格驱动测试

表格测试是一种编写更清晰的测试函数的方法;

顾名思义,表格驱动测试,就是指通过表格列举的方式来实现测试用例,表格中包含输入和预期输出,以及其他信息;这种方式是我们对测试的逻辑和思路更加清晰;

官方表格驱动测试的案例:

// fmt包中有如下一段测试代码:
// 定义测试的表格,包含了in输入字段和out期待输出字段
// 并且定义了该表格中的测试用例
// 然后使用t.Run的方式对每个用例进行测试
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)
   }
  })
 }
}

如果测试用例我们例举了非常多的话,我们希望测试用例可以并行执行,本身每个测试用例之间就是互不干扰的,因此上述代码可如下优化:

func TestFlagParser(t *testing.T) {
 var flagprinter flagPrinter
 for _, tt := range flagtests {
        ft := tt // 1. 重新声明变量,避免多个goroutine中使用了相同的变量
  t.Run(ft.in, func(t *testing.T) {
             t.Parallel()  // 2. 使用t.Parallel表示每个子测试之间能够彼此并行运行
   s := Sprintf(ft.in, &flagprinter)
   if s != ft.out {
    t.Errorf("got %q, want %q", s, ft.out)
   }
  })
 }
}

如上,既是表格驱动测试的用法;先定义表格以及测试用例,然后再通过t.Run子测试方式遍历表格;

自动生成表格驱动测试的代码gotests

gotests是一种自动生成表格驱动测试代码的工具;

使用案例:

  1. 比如在此前的一文搞定golang单元测试中,我们创建了split.go的业务文件;

  2. 安装gotests工具:$ go get -u github.com/cweill/gotests/...

  3. 执行命令:gotests -all -w split.go;关于gotests 的命令参数,大家可以去官网学习一下;

    • -all:生成所有的测试函数和方法
    • -w:输出测试结果到文件而不是控制台
  4. 生成的测试代码的格式如下,我们需要在todo的位置添加我们的测试逻辑即可:

    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)
       }
      })
     }
    }

go测试工具包--testfy

安装go get github.com/stretchr/testify;提供了更优雅的,灵活的,可mock的等等工具;

常用:testify/asserttestify/requiretestfy/mock

示例:

单元测试的时候,经常需要用到断言来检验测试结果,但是golang官方没有提供断言语法,导致我们可能会使用大量的ifelse语句;

testfy/assert为我们提供了很多常用的断言函数,让我们的测试代码实现的更加优雅;

比如在此前的一文搞定golang单元测试中我们检验TestSplit结果的方式如下:

if !reflect.DeepEqual(want, got) {
 t.Errorf("expected:%v, got:%v", want, got)
}

如果我们使用 testfy/assert的话,就可以如下简化:

// t是testing.T
assert.Equal(t, want, got)  // 使用assert提供的断言函数;

//或者如下使用方式,先创建assert对象:
assert := assert.New(t)

assert.Equal(123123"they should be equal")//是否相等测试
assert.NotEqual(123456"they should not be equal")//是否不等测试
assert.Nil(object)//是否nil测试
if assert.NotNil(object) {
    assert.Equal("Something", object.Value)
}

testfy中除了assert工具以外,还有常用的是testfy/require工具,以及还提供了mockhttp的工具,大家可以去官网了解一下;

什么是mock呢?比如我们的测试中有一个步骤是向用户成功发送邮件,事实上我们需要用户确认邮件后,才认为该邮件用户已确认;但实际测试中,我们不可能真的给用户发送邮件,或者说我们不可能每次测试都真的发送邮件(假如不是邮件而是短信的话,每次测试可都是需要花钱的),因此mock就可以模拟用户确认短信的行为,即模拟功能;

mock技术可用于各种不同的系统,例如模拟数据库查询或者是与其他API的交互等等,非常实用;


坚持每日输出go开发+面试题+算法+工作经验等后端相关技术

关于我今年的计划请查看:flag-2022

更多博客内容请查看原文链接


分类:

后端

标签:

后端

作者介绍

李二狗的星球
V1

李二狗的星球