Go语言的单元测试与基准测试详解

发表于:2023-12-20 09:39

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Palp1tate    来源:CSDN

  单元测试
  以一个加法函数为例,对其进行单元测试。
  首先编写add.go文件:
  //add.go
  package main
  func add(a, b int) int {
  return a + b
  }
  其次编写add_test.go文件,在go语言中,测试文件均已_test结尾,这里只需要在被测试的文件后加上_test即可。并且测试文件与要被测试的文件需要放在同一个包中,并不像Java那样需要将所有的测试文件放在一个专门的测试文件夹里面,例如我将这两个文件都放在main包下:
  package main
  import (
  "fmt"
  "testing"
  )
  //测试函数需要以Test开头
  func TestAdd(t *testing.T) {
  fmt.Println("Running short test")
  res := add(1, 2)
  if res != 3 {
  t.Errorf("add(1,2) should be 3, got %d", res)
  }
  }
  cd到测试文件的目录,执行测试命令go test:
  以下是运行结果:
  (base) PS F:\GolandProjects\GoProject1\main> go test
  Running short test
  PASS
  ok      GoProject1/main 0.489s
  如果想在测试中跳过那些需要耗时比较长的测试,可以做以下处理:
  package main
  import (
  "fmt"
  "testing"
  )
  func TestAdd(t *testing.T) {
  fmt.Println("Running short test")
  res := add(1, 2)
  if res != 3 {
  t.Errorf("add(1,2) should be 3, got %d", res)
  }
  }
  func TestAdd2(t *testing.T) {
  if testing.Short() {
  fmt.Println("Skipping long test")
          //短测试模式就跳过该测试
  t.Skip("Skipping long test")
  }
  fmt.Println("Running long test")
  res := add(5, 6)
  if res != 11 {
  t.Errorf("add(5,6) should be 11, got %d", res)
  }
  }
  在运行时指执行短测试,只需要执行go test -short:
  (base) PS F:\GolandProjects\GoProject1\main> go test -short
  Running short test
  Skipping long test
  PASS
  ok      GoProject1/main 0.448s
  我们发现跳过了第二个测试,也就是测试函数TestAdd2。
  当然如果还是执行go test命令,则两个测试都将会运行:
  (base) PS F:\GolandProjects\GoProject1\main> go test       
  Running short test
  Running long test
  PASS
  ok      GoProject1/main 0.417s
  如果想要同时测试很多条数据,可以按如下的方式处理,而不需要写很多的函数:
  func TestAdd3(t *testing.T) {
  var dataset = []struct {
  a, b, expected int
  }{
  {1, 2, 3},
  {5, 6, 11},
  {10, 20, 30},
  {100, 200, 300},
  }
  for _, d := range dataset {
  res := add(d.a, d.b)
  if res != d.expected {
  t.Errorf("add(%d,%d) should be %d, got %d", d.a, d.b, d.expected, res)
  }
  }
  }
  这里我们用go test -v测试一下:
  (base) PS F:\GolandProjects\GoProject1\main> go test -v
  === RUN   TestAdd
  Running short test
  --- PASS: TestAdd (0.00s)
  === RUN   TestAdd2
  Running long test
  --- PASS: TestAdd2 (0.00s)
  === RUN   TestAdd3
  --- PASS: TestAdd3 (0.00s)
  PASS
  ok      GoProject1/main 0.408s
  go test 用于运行测试并显示简洁的结果,而 go test -v 用于以详细模式运行测试并提供更多的输出信息,有助于更深入地了解测试的运行情况。通常,在开发和调试过程中,使用 -v 标志是很有帮助的,但在持续集成和自动化测试中,可能更倾向于使用简洁的 go test,以便更容易解释测试结果。
  基准测试
  性能表现需要实际数据衡量,Go语言提供了支持基准性能测试的benchmark工具。基准测试用于确定一段代码的执行速度和性能,并可以用来优化和改进代码。
  以编写斐波那契函数为例:
  //fib.go
  package main
  func Fib(n int) int {
      if n < 2 {
         return n
      }
      return Fib(n-1) + Fib(n-2)
  }
  //fib_test.go
  package main
  import (
      "testing"
  )
  func BenchmarkFib10(b *testing.B) {
      for i := 0; i < b.N; i++ {
         Fib(10)
      }
  }
  benchmark 和普通的单元测试用例一样,都位于 _test.go 文件中。
  函数名以 Benchmark 开头,参数是 b *testing.B。和普通的单元测试用例很像,单元测试函数名以 Test 开头,参数是t *testing.T。使用 b.N 控制循环次数:b.N 是基准测试的循环次数,它会根据不同的运行情况自动调整,以保证结果的可比性。
  ·运行当前 package 内的用例:go test .
  · 运行子 package 内的用例: go test ./<package name>
  · 如果想递归测试当前目录下的所有的 package:go test ./...
  go test 命令默认不运行 benchmark 用例的,如果我们想运行 benchmark 用例,需要加上 -bench 参数。例如:
  $ go test -bench .
  goos: windows
  goarch: amd64
  pkg: GoProject1
  cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
  BenchmarkFib10-16        5496252               212.5 ns/op
  PASS
  ok      GoProject1      1.454s
  ·goos: windows:这行显示运行基准测试的操作系统,此处为 Windows。
  · goarch: amd64:这行显示运行基准测试的机器架构,此处为 64 位 AMD 架构。
  · pkg: GoProject1:这行显示包含基准测试代码的包名,此处为 “GoProject1”。
  · cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz:这行显示运行基准测试的机器 CPU 信息,包括 CPU 型号和时钟频率。
  · PASS:这行表示所有的测试,包括基准测试,都已成功通过。
  · ok GoProject1 1.454s:这行显示所有测试,包括基准测试,的整体执行时间。在这种情况下,整个测试套件执行时间大约为 1.454 秒。
  · BenchmarkFib10-16 是测试函数名,-16表示GOMAXPROCS的值为16,GOMAXPROCS 1.5版本后,默认值为CPU核数 。5496252 表示一共执行5496252 次,即b.N的值。212.5 ns/op表示每次执行花费212.5ns。
  再举一个比较详细的例子,比较不同字符串处理方式的性能:
  func Plus(n int, str string) string {
      s := ""
      for i := 0; i < n; i++ {
         s += str
      }
      return s
  }
  func StrBuilder(n int, str string) string {
      var builder strings.Builder
      for i := 0; i < n; i++ {
         builder.WriteString(str)
      }
      return builder.String()
  }
  func ByteBuffer(n int, str string) string {
      buf := new(bytes.Buffer)
      for i := 0; i < n; i++ {
         buf.WriteString(str)
      }
      return buf.String()
  }
  func PreStrBuilder(n int, str string) string {
  var builder strings.Builder
  builder.Grow(n * len(str))
  for i := 0; i < n; i++ {
  builder.WriteString(str)
  }
  return builder.String()
  }
  func PreStrByteBuffer(n int, str string) string {
  buf := new(bytes.Buffer)
  buf.Grow(n * len(str))
  for i := 0; i < n; i++ {
  buf.WriteString(str)
  }
  return buf.String()
  }
  基准测试函数:
  func BenchmarkPlus(b *testing.B) {
  for i := 0; i < b.N; i++ {
  Plus(100000, "wxy")
  }
  }
  func BenchmarkStrBuilder(b *testing.B) {
  for i := 0; i < b.N; i++ {
  StrBuilder(100000, "wxy")
  }
  }
  func BenchmarkByteBuffer(b *testing.B) {
  for i := 0; i < b.N; i++ {
  ByteBuffer(100000, "wxy")
  }
  }
  func BenchmarkPreStrBuilder(b *testing.B) {
  for i := 0; i < b.N; i++ {
  PreStrBuilder(100000, "wxy")
  }
  }
  func BenchmarkPreByteBuffer(b *testing.B) {
  for i := 0; i < b.N; i++ {
  PreStrByteBuffer(100000, "wxy")
  }
  }
  以下是运行结果:
  $ go test -bench .
  goos: windows
  goarch: amd64
  pkg: GoProject1
  cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
  BenchmarkPlus-16                       1        1126084200 ns/op
  BenchmarkStrBuilder-16              3982            284773 ns/op
  BenchmarkByteBuffer-16              2947            485091 ns/op
  BenchmarkPreStrBuilder-16           4771            278961 ns/op
  BenchmarkPreByteBuffer-16           3310            364676 ns/op
  PASS
  ok      GoProject1      6.457s
  ·使用+拼接性能最差,strings.Builder,bytes.Buffer相近,strings.Builder更快
  · 字符串在Go语言中是不可变类型,占用内存大小是固定的
  · 使用+每次都会重新分配内存
  · strings.Builder, bytes.Buffer底层都是[]byte数组。内存扩容策略,不需要每次拼接重新分配内存
  · 预分配内存后,strings.Builder, bytes.Buffer性能都有所提升
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号