当前位置:   article > 正文

15.2 测试-网格测试、基准测试与测试覆盖率

15.2 测试-网格测试、基准测试与测试覆盖率

1. 网格测试

函数或方法的输出因收到的输入而异,如果为每个输入专门编写一个测试用例,将导致大量的重复代码。

不妨将输入的各种组合存放在网格之中,只编写一个测试用例即完成对所有输入的测试,比如象下面这样:

var greetingTests = []greetingTest{
        {"en-US", "George",   "Hello George!"},
        {"fr-FR", "Chloé",    "Bonjour Chloé!"},
        {"it-IT", "Giuseppe", "Ciao Giuseppe!"},
}

  1. // 网格测试
  2. // 源代码
  3. // Package greeting return greeting
  4. package greeting
  5. func translate(locale string) string {
  6.     switch locale {
  7.     case "en-US":
  8.         return "Hi "
  9.     case "fr-FR":
  10.         return "Bonjour "
  11.     case "it-IT":
  12.         return "Ciao "
  13.     default:
  14.         return "Hello "
  15.     }
  16. }
  17. // Greeting return greeting
  18. func Greeting(locale, name string) string {
  19.     return translate(locale) + name + "!"
  20. }
  1. // 源代码对应的测试用例(测试输入为我们之前定义的网格)
  2. // 函数或方法的输出因收到的输入而异,如果为每个输入专门编写一个测试用例,将导/// 致大量的重复代码。不妨将输入的各种组合存放在网格之中,只编写一个测试用例即
  3. // 完成对所有输入的测试
  4. package greeting
  5. import "testing"
  6. type greetingTest struct {
  7.     locale string
  8.     name string
  9.     expected string
  10. }
  11. var greetingTests = []greetingTest{
  12.     {"en-US", "George", "Hello George!"}, // 与源代码不一致,会失败
  13.     {"fr-FR", "Chloé", "Bonjour Chloé!"},
  14.     {"it-IT", "Giuseppe", "Ciao Giuseppe!"},
  15. }
  16. func TestGreeting(t *testing.T) {
  17.   for _, test := range greetingTests {
  18.     actual := Greeting(test.locale, test.name)
  19.       if actual != test.expected {
  20.         t.Fatalf(
  21. "Greeting(%q,%q)->%q, expected %q",
  22.           test.locale, test.name, actual,
  23. test.expected)
  24.         }
  25.     }
  26. }
  27. // 打印输出:
  28. greeting_test.go:29: Greeting("en-US","George")->"Hi George!", expected "Hello George!"

2. 基准测试

Go语言提供了功能强大的基准测试框架,利用该框架开发人员可以精确地获知,选择何种方式能使程序完成特定任务的性能最优。

基准测试函数的函数名以"Benchmark"开头,接受一个类型为B的参数。

  • func ByPlus(strs ...string) string { ... }
  • func BenchmarkByPlus(b *testing.B) { ... }

在测试函数内部,使用循环反复地调用被测试函数以建立基准。

  • for n := 0; n < b.N; n++ {
            ByPlus("Hello", " ", "World", "!")
    }
  • 循环的次数N无需指定,框架会自动设置它以获得可靠的数据集。(为了避免随机性,会执行N次求平均值来衡量性能)

基准测试结束后,会生成一个测试报告,指出每个被测函数的总调用次数(N)和单次调用的平均耗时(单位ns)。

  • BenchmarkByPlus-8        10000000        185 ns/op

在包目录下执行如下命令,启动基准测试:

  • go test -bench
  1. // 基准测试(测试了3种字符串拼接方法的效率)
  2. // Package joinstrs join strings
  3. package joinstrs
  4. import (
  5.     "bytes"
  6.     "strings"
  7. )
  8. // ByPlus join strings by plus
  9. func ByPlus(strs ...string) string {
  10.     s := ""
  11.     for _, str := range strs {
  12.         s += str
  13.     }
  14.     return s
  15. }
  16. // ByJoin join strings by join
  17. func ByJoin(strs ...string) string {
  18.     return strings.Join(strs, "")
  19. }
  20. // ByBuff join strings by buffer
  21. func ByBuff(strs ...string) string {
  22.     b := bytes.Buffer{}
  23.     for _, str := range strs {
  24.         b.WriteString(str)
  25.     }
  26.     return b.String()
  27. }

  1. // 基准测试(测试了3种字符串拼接方法的效率)
  2. // 基准测试关注代码的运行性能。基准测试
  3. // 函数的函数名必须以"Benchmark"开头
  4. //
  5. // 执行如下命令,启动基准测试:
  6. // go test -bench .
  7. package joinstrs
  8. import "testing"
  9. func TestByPlus(t *testing.T) {
  10.     // 使用了"testing包"小节所述的actual-expected模式
  11. actual, expected := ByPlus(
  12.        "Hello", " ", "World", "!"),
  13. "Hello World!"
  14.     if actual != expected {
  15.         t.Fatalf("Actual %q, expected %q",
  16. actual, expected)
  17.     }
  18. }
  19. func TestByJoin(t *testing.T) {
  20.     actual, expected := ByJoin(
  21. "Hello", " ", "World", "!"),
  22. "Hello World!"
  23.     if actual != expected {
  24.         t.Fatalf("Actual %q, expected %q",
  25. actual, expected)
  26.     }
  27. }
  28. func TestByBuff(t *testing.T) {
  29.     actual, expected := ByBuff(
  30. "Hello", " ", "World", "!"),
  31. "Hello World!"
  32.     if actual != expected {
  33.         t.Fatalf("Actual %q, expected %q",
  34. actual, expected)
  35.     }
  36. }
  37. func BenchmarkByPlus(b *testing.B) { // 基准测试
  38.     for n := 0; n < b.N; n++ {
  39.         ByPlus("Hello", " ", "World", "!")
  40.     }
  41. }
  42. func BenchmarkByJoin(b *testing.B) {// 基准测试
  43.     for n := 0; n < b.N; n++ {
  44.         ByJoin("Hello", " ", "World", "!")
  45.     }
  46. }
  47. func BenchmarkByBuff(b *testing.B) {// 基准测试
  48.     for n := 0; n < b.N; n++ {
  49.         ByBuff("Hello", " ", "World", "!")
  50.     }
  51. }
  52. // 打印输出:
  53. goos: windows
  54. goarch: amd64
  55. pkg: test/benchmark
  56. BenchmarkByPlus-8 10000000 185 ns/op
  57. BenchmarkByJoin-8 20000000 103 ns/op//字符串拼接strings.Join()是最高效的
  58. BenchmarkByBuff-8 10000000 161 ns/op
  59. PASS
  60. ok test/benchmark 6.479s

3.测试覆盖率

测试覆盖率指被测试执行的代码总代码百分比。

  • 单元测试通过,说明功能正确
  • 基准测试效果也不错,说明性能可以接受
  • 这并不表示被测软件经受住了考验,因为有些代码可能根本就没有被执行到

在包目录下执行如下命令,计算测试覆盖率:

  • go test -cover
  • 其是针对某一包目录下的所有go源文件中的func进行计算的
  1. // 测试覆盖率指被测试执行的代码占总代码的百分比
  2. //
  3. // 执行如下命令,启动测试并输出覆盖率:
  4. // go test -cover
  5. package greeting
  6. import "testing"
  7. func TestGreeting(t *testing.T) {
  8.     actual, expected := Greeting("World"),
  9. "Hello World!"
  10.     if actual != expected {
  11.         t.Fatalf("Actual %q, expected %q",
  12. actual, expected)
  13.     }
  14. }
  15. // Package greeting return greeting
  16. package greeting
  17. // Greeting return greeting
  18. func Greeting(s string) string {
  19.     return "Hello " + s + "!"
  20. }
  21. // Farewell return farewell
  22. func Farewell(s string) string {
  23.     return "Goodbye " + s + "!"
  24. }
  25. // 打印输出:
  26. PASS
  27. coverage: 50.0% of statements
  28. ok test/coverage 0.447s

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号