Go 单元测试和性能测试

栏目: 编程工具 · 发布时间: 5年前

内容简介:测试对于互联网应用软件开发来说非常重要,它对软件可靠性保证具有重要意义,通过测试能够尽可能发现并改正软件中的错误,提高软件质量。这里我们主要讲解Go语言如何实现单元测试和性能测试。go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中的测试框架类似,你可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例,那么接下来让我们一一来看一下怎么写。

测试对于互联网应用软件开发来说非常重要,它对软件可靠性保证具有重要意义,通过测试能够尽可能发现并改正软件中的错误,提高软件质量。

这里我们主要讲解 Go 语言如何实现单元测试和性能测试。

go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中的测试框架类似,你可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例,那么接下来让我们一一来看一下怎么写。

单元测试

创建目录test,在目录下创建add.go、add_test.go两个文件,add_test.go为单元测试文件。

add_test.go

package test

import "testing"

func TestAdd(t *testing.T) {
   sum := Add(1, 2)
   if sum == 3 {
      t.Log("the result is ok")
   } else {
      t.Fatal("the result is wrong")
   }
}
func TestAdd1(t *testing.T) {
    t.Error("the result is error")
}

add.go

package test

func Add(a, b int) int {
   return a + b
}

然后在项目目录下运行 go test -v 就可以看到测试结果了

=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
    add_test.go:8: the result is ok
=== RUN   TestAdd1
--- FAIL: TestAdd1 (0.00s)
    add_test.go:14: the result is error
FAIL
exit status 1
FAIL    _/D_/gopath/src/ados/test       0.419s

如果看到PASS字样证明测试通过,FAIL字样表示测试失败。

使用testing库的测试框架需要遵循以下几个规则如下:

  • 文件名必须是 _test.go 结尾的,这样在执行 go test 的时候才会执行到相应的代码
  • 你必须import testing 这个包
  • 所有的测试用例函数必须是 Test 开头
  • 测试用例会按照源代码中写的顺序依次执行
  • 测试函数 TestXxx() 的参数是 testing.T ,我们可以使用该类型来记录错误或者是测试状态
  • 测试格式: func TestXxx (t *testing.T) , Xxx 部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如 Testintdiv 是错误的函数名。
  • 函数中通过调用 testing.TError , Errorf , FailNow , Fatal , FatalIf 方法,说明测试不通过,调用 Log 方法用来记录测试的信息。

性能测试或压力测试

压力测试用来检测函数(方法)的性能,和编写单元功能测试的方法类似,此处不再赘述,但需要注意以下几点:

  • 压力测试用例必须遵循如下格式,其中XXX可以是任意字母数字的组合,但是首字母不能是小写字母

    func BenchmarkXXX(b *testing.B) { ... }
  • go test 不会默认执行压力测试的函数,如果要执行压力测试需要带上参数 -test.bench ,语法: -test.bench="test_name_regex" ,例如 go test -test.bench=".*" 表示测试全部的压力测试函数
  • 在压力测试用例中,请记得在循环体内使用 testing.B.N ,以使测试可以正常的运行
  • 文件名也必须以 _test.go 结尾

在test目录下创建 reflect_test.go

package test

import (
   "reflect"
   "testing"
)

type Student struct {
   Name  string
   Age   int
   Class string
   Score int
}

func BenchmarkReflect_New(b *testing.B) {
   var s *Student
   sv := reflect.TypeOf(Student{})
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      sn := reflect.New(sv)
      s, _ = sn.Interface().(*Student)
   }
   _ = s
}
func BenchmarkDirect_New(b *testing.B) {
   var s *Student
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      s = new(Student)
   }
   _ = s
}
func BenchmarkReflect_Set(b *testing.B) {
   var s *Student
   sv := reflect.TypeOf(Student{})
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      sn := reflect.New(sv)
      s = sn.Interface().(*Student)
      s.Name = "Jerry"
      s.Age = 18
      s.Class = "20005"
      s.Score = 100
   }
}
func BenchmarkReflect_SetFieldByName(b *testing.B) {
   sv := reflect.TypeOf(Student{})
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      sn := reflect.New(sv).Elem()
      sn.FieldByName("Name").SetString("Jerry")
      sn.FieldByName("Age").SetInt(18)
      sn.FieldByName("Class").SetString("20005")
      sn.FieldByName("Score").SetInt(100)
   }
}
func BenchmarkReflect_SetFieldByIndex(b *testing.B) {
   sv := reflect.TypeOf(Student{})
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      sn := reflect.New(sv).Elem()
      sn.Field(0).SetString("Jerry")
      sn.Field(1).SetInt(18)
      sn.Field(2).SetString("20005")
      sn.Field(3).SetInt(100)
   }
}
func BenchmarkDirect_Set(b *testing.B) {
   var s *Student
   b.ResetTimer()
   for i := 0; i < b.N; i++ {
      s = new(Student)
      s.Name = "Jerry"
      s.Age = 18
      s.Class = "20005"
      s.Score = 100
   }
}

在test目录下,执行:

go test reflect_test.go -test.bench=".*"

结果如下

goos: windows
goarch: amd64
BenchmarkReflect_New-4                  20000000                84.9 ns/op
BenchmarkDirect_New-4                   30000000                50.6 ns/op
BenchmarkReflect_Set-4                  20000000                89.9 ns/op
BenchmarkReflect_SetFieldByName-4        3000000               552 ns/op
BenchmarkReflect_SetFieldByIndex-4      10000000               132 ns/op
BenchmarkDirect_Set-4                   30000000                53.0 ns/op
PASS
ok      command-line-arguments  10.982s

上面的结果显示我们没有执行任何 TestXXX 的单元测试函数,显示的结果只执行了压力测试函数,以第三行为例

BenchmarkReflect_New 函数执行了20000000次,每次的执行平均时间是84.9纳秒。最后一行 command-line-arguments 10.982s,代表总的执行时间为 10.982s。

如果只想对某个函数测试,以BenchmarkReflect_New 为例,执行命令

go test reflect_test.go -test.bench="BenchmarkReflect_New"

结果为:

goos: windows
goarch: amd64
BenchmarkReflect_New-4          20000000                84.9 ns/op
PASS
ok      command-line-arguments  2.490s

如果测试整个目录下的所有测试执行:

go test -test.bench=".*"

如果想显示内存分配的次数和大小添加 -benchmem

go test reflect_test.go -benchmem -test.bench=".*"

goos: windows
goarch: amd64
BenchmarkReflect_New-4                  20000000   88.3 ns/op  48 B/op     1 allocs/op
BenchmarkDirect_New-4                   30000000   53.8 ns/op  48 B/op     1 allocs/op
BenchmarkReflect_Set-4                  20000000   90.9 ns/op  48 B/op     1 allocs/op
BenchmarkReflect_SetFieldByName-4        3000000   564 ns/op   80 B/op     5 allocs/op
BenchmarkReflect_SetFieldByIndex-4      10000000   135 ns/op   48 B/op     1 allocs/op
BenchmarkDirect_Set-4                   30000000   52.4 ns/op  48 B/op     1 allocs/op
PASS
ok      command-line-arguments  12.955s

后两列代表分配的内存大小和次数(48 B/op 1 allocs/op)

推荐 gotests

它是编写Go测试的一个Golang命令行工具,可以根据目标源文件的函数和方法签名生成 表驱动的测试 。将自动导入测试文件中的任何新依赖项。

参考:

https://studygolang.com/stati...

https://www.cnblogs.com/yjf51...

links


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

正则表达式必知必会

正则表达式必知必会

Ben Forta / 杨涛、王建桥、杨晓云 / 人民邮电出版社 / 2007 / 29.00元

正则表达式是一种威力无比强大的武器,几乎在所有的程序设计语言里和计算机平台上都可以用它来完成各种复杂的文本处理工作。本书从简单的文本匹配开始,循序渐进地介绍了很多复杂内容,其中包括回溯引用、条件性求值和前后查找,等等。每章都为读者准备了许多简明又实用的示例,有助于全面、系统、快速掌握正则表达式,并运用它们去解决实际问题。 本书适合各种语言和平台的开发人员。一起来看看 《正则表达式必知必会》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器