19-单元测试

8 阅读3分钟

单元测试

  1. 什么是单元测试?
  • 指在计算机编程中针对一块特定的模块、组件、方法进行测试以验证其是否满足业务、质量需求的测试方法
  1. 怎么写单元测试?
  • 单元测试关键组成部分
    • 预备案例
    • 预期结果
    • 组件调用
    • 衡量预算
  • Golang 针对单元测试,有2种测试方式
    • 黑盒测试
      • 测试案例和源码在同一个目录下,但是不在同一个包中
      • 测试案例仅针对目标包里暴露出来的共有方法进行测试
    • 白盒测试
      • 测试案例和源码在同一个目录下,同一个包中
      • 测试案例可以看到同一个包中的私有变量、方法,可以针对每一项进行测试
  • Golang的单元测试编写规则
    • 文件名必须以_test.go结尾
    • 必须符合命名规则func TestXxxx(t *testing.T){},首单词Test
      • testing 是golang的内置包,用来做单元测试的
    • 对预期失败的case,需要调用
      • t.Fail()、t.FailNow()、t.Error(...)、t.Errorf()、t.Fatal(...)、t.Fatalf(...)等来声明失败
package calctest

import (
    "testing"
)
func TestMain(t *testing.T) {
    height, weight := 1.0, 1.0
    output := 1.0
    t.Log("开始计算,输入height: %f, weight: %f", height, weight)
    actualOutput, err := CalcBMI(height, weight)

    if err != nil {
        t.Errorf("计算出错了%v", err)
    }
    if actualOutput != output {
        t.Errorf("计算出错了actualOutput: %f, output: %f", actualOutput, output)
        // t.Fail()//测试失败
    }
}
  1. 怎么运行单元测试?
  • 运行命令:go test <package path>
    • 后面指的是文件夹路径
  1. 怎么单步调试单元测试?
  • 配合开发工具使用

单元测试

  • 用来验证函数逻辑是否正确
  • 文件名称改为 xxx_test.go
  • 文件内部必须有 Test为前缀的函数

例子1 bytedance/sonic 和 json的单元测试

  • 通过 go test xxx_test.go来执行单元测试
    • go test xxx_test.go -v -> -v让Println可正常执行
    • 默认 fmt.Println是不会输出的
  • 如果只想运行某个/某类 测试函数写法如下
    • go test xxx_test.go -v -run=TestJson-> 只运行TestJson函数
    • go test xxx_test.go -v -run=Json -> 回去找匹配的函数执行
package main

import (
	"encoding/json"
    "bytedance/sonic"
	"fmt"
    "testing"
)

type Student struct {
	Name   string
	Age    int
	Gender bool
}

type Class struct {
	Id       string
	Students []Student
}

var (
    s = Student{"张三", 18, true}
    c = Class{
        Id: "001",
        Students: []Student{s, s}
    }
)

func TestJson(t *testing.T) {
    bytes, err := json.Marshal(c)
    if err != nil {
        t.Fail()//就会终止下面的执行
    }
    var c2 Class
    err = json.Unmarshal(bytes, &c2)
    if err != nil {
        t.Fail()//就会终止下面的执行
    }
    if !(c.Id == c2.Id  && len(c.Students) == len(c2.Students)) {
        t.Fail()
    }
    fmt.Println("json测试通过")
}

func JsonSonic(t *testing.T) {
    bytes, err := sonic.Marshal(c)
    if err != nil {
        t.Fail()//就会终止下面的执行
    }
    var c2 Class
    err = sonic.Unmarshal(bytes, &c2)
    if err != nil {
        t.Fail()//就会终止下面的执行
    }
    if !(c.Id == c2.Id  && len(c.Students) == len(c2.Students)) {
        t.Fail()
    }
    fmt.Println("sonic测试通过")
}

基准测试

  • 用来做性能测试的,会返回执行
  • 文件名称改为 xxx_test.go
  • 文件内部必须有 Benchmark为前缀的函数

例子2 bytedance/sonic 和 json的性能测试

  • go test -bench=Json json_test.go
    • 加 -benchmem可以获取内存情况
package main

import (
	"encoding/json"
    "bytedance/sonic"
	"fmt"
    "testing"
)

type Student struct {
	Name   string
	Age    int
	Gender bool
}

type Class struct {
	Id       string
	Students []Student
}

var (
    s = Student{"张三", 18, true}
    c = Class{
        Id: "001",
        Students: []Student{s, s}
    }
)

func BenchmarkJson(b *testing.B) {
    // b.N 不是一个常量 会根据实际的运行耗时动态取值,N是个很大的数
    for i := 0; i < b.N; i++ {
        // 不关心逻辑是否正确
        bytes, _ := json.Marshal(c)
        var c2 Class
        json.Unmarshal(bytes, &c2)
        fmt.Println("json测试通过")
    }
}

func BenchmarkSonic(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 不关心逻辑是否正确
        bytes, _ := sonic.Marshal(c)
        var c2 Class
        sonic.Unmarshal(bytes, &c2)
        fmt.Println("json测试通过")
    }
}