资讯专栏INFORMATION COLUMN

Go 单元测试和性能测试

SimonMa / 2912人阅读

摘要:测试对于互联网应用软件开发来说非常重要,它对软件可靠性保证具有重要意义,通过测试能够尽可能发现并改正软件中的错误,提高软件质量。这里我们主要讲解语言如何实现单元测试和性能测试。单元测试创建目录,在目录下创建两个文件,为单元测试文件。

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

这里我们主要讲解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

目录

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/8916.html

相关文章

  • Go单元测试

    摘要:单元测试简介单元测试是语言级别提供的完整功能,测试代码以命名,单元测试的以开头,性能测试以开头,运行测试命令例子实现排列组合函数以及对应的单元测试和性能测试创建工程目录结构目录结构说明参见规范项目实现排列组合函数实现单元测试和性能测 Go单元测试 简介 单元测试是go语言级别提供的完整功能,测试代码以*_test.go命名,单元测试的case以Test开头,性能测试case以Bench...

    beanlam 评论0 收藏0
  • Go 语言 Excel 类库 Excelize 2.0.0 版本发布

    摘要:是语言编写的用于操作文档类库,基于标准。可以使用它来读取写入由及以上版本创建的文档。年月日,社区正式发布了版本,该版本包含了很多新功能错误修复和性能优化。 showImg(https://segmentfault.com/img/remote/1460000018971694?w=880&h=360); Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库...

    andycall 评论0 收藏0
  • 三年前旧代码的重构、总结与反思

    摘要:最近在维护一个三年前的旧代码,用的是框架。单元测试和语言并发控制实际上是个蛋疼的问题,夸张一点说,当时的并不能特别轻松地实现并发,甚至不能实现并发。语言的功能之一就是自带单元测试。用语言之前,我的习惯是不写单元测试。 最近在维护一个三年前的旧代码,用的是laravel框架。 从某些方面来讲,这个代码算是比较标准为了实现在规定的时间内完成相关功能,同时程序员水平不高、经过大量优化之后,变...

    Shihira 评论0 收藏0
  • 后端好书阅读与推荐(续七)

    摘要:持续交付持续交付豆瓣微服务离不开,而核心就是几点自动化连续小范围快速可靠。敏捷革命敏捷革命提升个人创造力与企业效率的全新协作模式豆瓣实际上正是敏捷开发的最佳实践,有了前面的铺垫,我们可以通过这本书我们来真正了解敏捷开发的全貌。 后端好书阅读与推荐系列文章: 后端好书阅读与推荐后端好书阅读与推荐(续)后端好书阅读与推荐(续二)后端好书阅读与推荐(续三)后端好书阅读与推荐(续四)后端好书...

    zollero 评论0 收藏0
  • 编写可测试Go代码

    摘要:功能测试函数功能测试函数需要接收类型的单一参数,类型用来管理测试状态和支持格式化的测试日志。测试函数的相关说明,可以通过来查看帮助文档。下面是一个例子被测试的进程退出函数测试进程退出函数的测试函数参考资料原文链接 原文链接:http://tabalt.net/blog/golang... Golang作为一门标榜工程化的语言,提供了非常简便、实用的编写单元测试的能力。本文通过Golan...

    khlbat 评论0 收藏0

发表评论

0条评论

SimonMa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<