首页 > 其他分享 >代码测试

代码测试

时间:2023-01-30 19:24:38浏览次数:36  
标签:代码 Call mock 测试 测试函数 go store

go语言通过自带的testing框架,可以用来实现单元测试与性能测试,通过go test命令来执行单元测试或性能测试。

go test执行单元测试是以包为单位的,如果没有指定包,则默认使用执行命令时所在的包。遍历包下以*_test.go结尾的文件,执行以TestBenchmark, Example开头的测试函数。

单元测试

单元测试用例函数以Test开头,例如TestXxx或者Test_xxx。函数的参数必须是testing.T,可以调用testing.T的Error 、Errorf 、FailNow 、Fatal 、FatalIf 方法,来说明测试不通过;调用 Log 、Logf 方法来记录测试信息。

执行go test命令就会执行所在包下的所有测试函数。在测试过程中还有如下常用的参数:

  • -v:显示测试函数的运行细节。
  • -run <regexp>:指定要执行的测试函数
  • -count N:指定测试函数指定的次数

单元测试模板

测试一个函数,我们通过会测试多个输入与输出是否正确,测试函数的编写也有了较为通用的模板

func TestFunc(t *testing.T) {
	// 开始写单元测试逻辑
	type args struct {
		// 被测试函数的参数
	}
	tests := []struct {
		name    string
		args    args
		want TYPE //args作为参数的时候,被测试函数预期的输出结果
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		if got := Func(tt.args.x, tt.args.y); got != tt.want {
			t.Errorf("Func(args.x, args.y) = %f, want %v", got, tt.want)
		}
	}
}

具体的细节可能有所不同,但是一般较为规范的单元测试都符合上面的格式。

在上面的got != tt.want的比较,我们可以使用github.com/stretchr/testify/assert 包来使得比较更加方便和简单。

单元测试自动化生成

单元测试既然可以有通用的模板,那么当然就可以有工具来帮助我们生成这个模板。比如gotests这个工具。

执行如下命令安装gotests

$ go get -u github.com/cweill/gotests/...

gotests 命令执行格式为:gotests [options] [PATH] [FILE] ...。gotests 可以为PATH下的所有 Go 源码文件中的函数生成测试代码,也可以只为某个FILE中的函数生成测试代码。

例如$ gotests -all -w .命令就会为当前目录下的所有函数生成测试代码。然后只需要在模板的TODO位置出添加具体的case即可。

性能测试

性能测试函数必须以Benchmark开头,例如BenchmarkXxx或者Benchmark_xxx。并且其函数参数必须为b testing.B,函数内b.N作为循环测试,其中N会在运行时动态调整,直到性能测试函数能够运行足够长的时间,来进行可靠的计时。

func BenchmarkRandInt(b *testing.B) {
    for i := 0; i < b.N; i++ {
        RandInt()
    }
}

go test命令默认不会执行性能测试函数,需要通过参数-bench <regexp>来运行指定的测试函数,go test -bench=".*"表示执行所有的性能测试函数。

在性能测试中,如果被测试函数执行前需要进行一些耗时的准备操作,那么可以在准备工作完成后重置计时,或者先暂停计时准备工作完成后再启动计时。


func BenchmarkXxx(b *testing.B) {
	// 准备工作
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
		//...
    }
}

func BenchmarkXxx(b *testing.B) {
	b.StopTimer() // 调用该函数停止压力测试的时间计数  
	// 准备工作
	b.StartTimer() // 重新开始时间
    for i := 0; i < b.N; i++ {
		//...
    }
}

性能测试中我们还关注如下的参数

  • -benchmem:性能测试中关注的内存指标。如 每次执行分配的内存大小(越小,占用内存越少)以及每次执行内存的分配次数(越小,性能越好)。
  • -benchtime:指定测试时间和循环执行测试,格式为Nx,例如10s表示10秒,100x表示执行100次。
  • -cpu:指定 GOMAXPROCS。
  • -timeout:指定测试函数的超时时间

示例测试

示例测试函数以Example开头。例如ExampleXxx或者Example_xxx。示例测试函数没有输入参数和输出参数,但是在函数的结尾可能会有以Output:或者Unordered output:开头的注释,Unordered output:开头的注释会忽略输出行的顺序。例如

func ExampleMax() {
    fmt.Println(Max(1, 2))
    // Output:
    // 2
}

go test命令默认也会执行示例测试函数,并且将示例测试函数输出到标准输出的内容与注释的内容进行比较(比较时忽略前后空格),相等则测试通过,不相等则测试失败。

对于大型示例测试,可以一个测试函数一个文件,这样godoc展示这类示例测试的时候会直接展示整个文件。

TestMain函数

func TestMain(m *testing.M) {
    fmt.Println("do some setup")
    m.Run()
    fmt.Println("do some cleanup")
}

TestMain是一个特殊的函数,go test命令执行测试函数的时候,会先执行TestMain函数,TestMain中调用m.Run()来执行普通的测试函数。所以TestMain函数,我们通过在m.Run()之前做一些测试的准备工作,例如创建数据库连接;在m.Run()做一些测试的清理工作,例如关闭数据库连接,删除测试产生的临时文件。

mock测试

一半来说,数据库中是不允许有外部依赖的,例如数据库连接,这些外部依赖都需要被模拟,在go中,就是借用各种mock工具进行模拟的。

GoMock是golang官方开发的测试框架,实现较为完整的基于interface的Mock功能。接下来我们就了解一下使用GoMock进行mock测试。

GoMock测试框架分为两部分,一是GoMock包,用来管理对象生成周期,另一个就是mockgen工具,用来生成interface对应的mock源文件。

首先,安装gomock包和mockgen工具

$ go get github.com/golang/mock/gomock
$ go install github.com/golang/mock/mockgen

GoMock是基于interface的,所以我们现在有如下的接口Store,其中只有一个方法GetCount用于获取记录条数。我们可以为不同的数据库来实现这个接口。

type Store interface {
	GetCount() int
}

然后有一个函数调用了这个接口实现的方法

func Count(store Store) int {
	return store.GetCount()
}

我们现在要对Count这个函数进行单元测试,但是现在Store接口既没有实现,单元测试环境中也不应该依赖外部数据库,所以我们要对Store进行mock来获得一个实例。

对接口进行mock,这就要用到mockgen工具了。当前demo的module名为gomock-demo,目录结构如下:

├── count.go
├── go.mod
├── go.sum
└── store
    └── store.go

执行下面的命令为Store接口生成mock实现,命令参数后续进行说明

$ mockgen -destination store/mock/store_mock.go -package store gomock-demo/store Store

执行结束后的目录结构如下:

├── count.go
├── go.mod
├── go.sum
└── store
    ├── mock
    │   └── store_mock.go
    └── store.go

我们接下来就可以为Count编写如下的单元测试了

func TestCount(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockStore := store.NewMockStore(ctrl)
	mockStore.EXPECT().GetCount().Return(10)

	got := Count(mockStore)
	if got != 10 {
		t.Error("Count wrong res: ", got)
	}
}

通过mock,很多无法测试的函数也可以进行测试了。

mockgen说明

mockgen工具用于为接口生成mock实现。有两种生成方式,一种是带-source的源码模式,例如

mockgen -destination store/mock/store_mock.go -package store -source store/store.go

还有一种就是利用反射程序的反射模式,通过传递两个非标志参数,即导入路径和逗号分隔的接口列表来启用,其他参数和源码模式共用。前面的示例中使用的就是反射模式

$ mockgen -destination store/mock/store_mock.go -package store gomock-demo/store Store

这里介绍再一下命令行的参数即可

  • -destination:生成的mock代码所在路径
  • -package;mock文件的包名
  • -source:需要mock的接口文件
  • -imports:依赖的包
  • -aux_files:接口不止一个文件时,附件文件
  • -build_flags:传递给build工具的参数
    通常前3个参数就已经够用了。

为了方便,可以在接口文件的代码添加注释

//go:generate mockgen -destination mock/store_mock.go -package store -source store.go

这样我们只需要执行下面的命令,就可以为这个接口文件生成mock代码了

go generate ./...

mock代码编写单元测试

通过mockgen工具生成mock代码之后,我们就可以利用生成的mock代码和gomock包编写单元测试了。

首先,创建Mock控制器来管理整个mock过程,并在完成之后进行回收

ctrl := gomock.NewController(t)
defer ctrl.Finish()

然后创建mock实例

mockStore := store.NewMockStore(ctrl)

获得mock实例之后,要想mock一个接口,就需要mock接口的入参和返回值。返回值通过Return来mock,就像前面的示例一样

mockStore.EXPECT().GetCount().Return(10)

这里的GetCount没有入参,但是对于有入参的方法,这里需要使用如下的参数匹配对入参进行约束

  • gomock.Any(),可以用来表示任意的入参。
  • gomock.Eq(value),用来表示与 value 等价的值。
  • gomock.Not(value),用来表示非 value 以外的值。
  • gomock.Nil(),用来表示 None 值。

mock实例进行EXPECT断言,然后调用方法就可以获得第一个Call对象,并对其进行约束

func (c *Call) After(preReq *Call) *Call // After声明调用在preReq完成后执行
func (c *Call) Times(n int) *Call // 设置调用次数为 n 次
func (c *Call) AnyTimes() *Call // 允许调用次数为 0 次或更多次
func (c *Call) MaxTimes(n int) *Call // 设置最大的调用次数为 n 次
func (c *Call) MinTimes(n int) *Call // 设置最小的调用次数为 n 次
func (c *Call) Do(f interface{}) *Call // 声明在匹配时要运行的操作
func (c *Call) Return(rets ...interface{}) *Call //  // 声明模拟函数调用返回的值
func (c *Call) SetArg(n int, value interface{}) *Call // 声明使用指针设置第 n 个参数的值

测试覆盖率

为了避免漏掉为某些函数编写测试用例,或者测试用例不够全面。Go提供了cover工具来统计测试覆盖率。

  • 生成测试覆盖率数据
$ go test -coverprofile=coverage.out

会在当前目录下生成coverage.out覆盖率数据文件。

  • 分析覆盖率文件
$ go tool cover -func=coverage.out

可以查看各个函数的测试覆盖率。可以据此来为函数编写或完善测试用例。

为了更清晰地展示,还可以生成html格式的分析文件

$ go tool cover -html=coverage.out -o coverage.html

标签:代码,Call,mock,测试,测试函数,go,store
From: https://www.cnblogs.com/smarticen/p/17076767.html

相关文章

  • 0141-Go-单元测试
    环境Time2022-08-25Go1.19前言说明参考:https://gobyexample.com/testing-and-benchmarking目标使用Go语言进行测试。示例packagemainimport("fmt"......
  • linux基础:3、常用功能代码
    目录一、⽹络不通排查流程二、etc⽬录下重要的数据⽂件三、usr⽬录下重要的数据⽂件四、var⽬录下重要的数据⽂件五、proc⽬录重要的数据⽂件六、系统优化相关七、环境变量......
  • Web应用模式 API接口 接口测试工具postman及使用
    目录Web应用模式前后端混合开发前后端分离开发API接口接口测试工具postmanpostman的使用Web应用模式前后端混合开发DjangoWeb框架,专门用来写web项目之前所学的,写的BB......
  • API接口与接口测试工具postman
    目录学习资料一、web应用模式1.web项目的模式2.前后端混合开发模式3.前后端分离开发模式二、API接口1.简介:2.api接口的内容3.接口案例三、接口测试工具postman1.浏览器与接......
  • 使用 JMeter 进行压力测试
    目录 文章目录    jdk,jmeter下载安装    jmeter应用启动    jmeter使用教程    jmeter测试结果分析一jdk,jmeter下载安装1.......
  • drf-api接口、测试工具postman
    1.web应用模式"""django是一个web框架,专门用来写web项目,之前学的bbs项目,图书管理系统,用的是前后端混合开发。"""前后端混合模式:1.后端开发:写后端,也要写模板语......
  • JMeter-InfluxDB-Grafana之性能测试
    一、工具介绍JMeter是一个开源的性能测试工具,使用Java语言开发,可对服务器、网络或对象模拟巨大的负载,在不同压力类别下测试它们的强度和分析整体性能。InfluxDB是一个......
  • 手机测试之-monkey
    一、Monkeymonkey就是猴子, monkey测试,就像一只猴子,在电脑面前,乱敲键盘在测试,猴子什么都不懂,只知道乱敲通过Monkey程序模拟用户触摸屏幕、滑动Trackball、按键等操......
  • 在python代码中,写其他编程语言的hello world
    1.helloworld不论哪种编程语言,在你最开始学习时,都会给你一个在终端输出helloworld的示例print("helloworld")这已经成为一种惯例,最近在github上闲逛时,偶遇了一个特别有......
  • 测试人员如何做不漏测?
    什么是漏测?具体地说,什么是测试漏测?测试漏测是指软件产品在测试结束后出现了在测试过程中没有被发现的bug。我们知道,漏测是每一个软件测试者最头疼的事,一旦出现漏测,首先给......