手记

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(123, 123, "they should be equal")//是否相等测试
assert.NotEqual(123, 456, "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开发+面试题+算法+工作经验等后端相关技术
更多博客内容请查看bigshake

0人推荐
随时随地看视频
慕课网APP