golang 单元测试

文章目录 (?) [+]

    单元测试编写原则

    测试文件必须以 _test.go 结尾。

    测试文件包名要和被测试函数所在包一致。

    必须引入 testing 包。

    单元测试函数必须以 Test 开头。

    使用案例函数必须以 Example 开头,且有预期 Output 注释。

    基准测试函数必须以 Benchmark 开头。

    常用测试命令

    # 单元测试
    go test \
        -v \ # 显示详细信息
        -run=. # 只运行名称符合正则的单元测试和案例
        -count=2 \ # 指定每个测试运行次数
        mailer_test.go
    
    # 基准测试
    go test \
        -v \ # 显示详细信息
        -bench=. \ # 指定待测试的 Benchmark 的正则匹配
        -benchtime=5s \ # 指定每个基准测试运行时间,默认 1s,不指定则为自动设置
        -count=2 \ # 指定每个测试运行次数
        -benchmem \ # 显示内存分配
        benchmark_test.go
    
    # 编译测试文件
    go test \
        -c \ # 编译测试文件
        -o /tmp/gotest # 指定编译输出的文件位置,不指定则为 ./<package-name>.test
    
    # 使用编译的测试文件进行单元测试
    ./<package-name>.test \
        -test.v \ # 输出详细信息
        -test.run . \ # 只运行名称符合正则的单元测试和案例
        -test.count 2 # 指定每个测试运行次数,默认 1
    
    # 使用编译的测试文件进行基准测试
    ./<package-name>.test \
        -test.v \ # 输出详细信息
        -test.bench . \ # 只运行名称符合正则的基准测试
        -test.benchmem \ # 基准测试时输出内存分配
        -test.benchtime 5s \ # 指定每个基准测试运行时间,默认 1s,不指定则为自动设置
        -test.count 2 # 指定每个测试运行次数,默认 1
    
    # 覆盖率测试与分析
    go test -coverprofile=c.out
    go tool cover -html=c.out
    
    # 基准测试与分析
    sudo apt install graphviz -y
    go test -bench . -cpuprofile cpu.out
    go tool pprof cpu.out
    (pprof) web

    案例

    add.go

    package main
    
    import "fmt"
    
    type Number struct {
        a int
        b int
    }
    
    func (num *Number) Multi() int {
        return num.a * num.b
    }
    
    func main() {
        num := Number{
            a: 2,
            b: 6,
        }
        fmt.Println(num.Multi())
    }

    add_test.go

    package main
    
    import (
        "fmt"
        "testing"
    )
    
    // 单元测试
    func TestNumber_Multi(t *testing.T) {
        type fields struct {
            a int
            b int
        }
        tests := []struct {
            name   string
            fields fields
            want   int
        }{
            {name: "1", fields: fields{a: 0, b: 2}, want: 0},
            {name: "2", fields: fields{a: 3, b: -2}, want: -6},
            {name: "3", fields: fields{a: 5, b: 9}, want: 45},
        }
        for _, tt := range tests {
            t.Run(tt.name, func(t *testing.T) {
                num := &Number{
                    a: tt.fields.a,
                    b: tt.fields.b,
                }
                if got := num.Multi(); got != tt.want {
                    t.Errorf("Multi() = %v, want %v", got, tt.want)
                }
            })
        }
    }
    
    // 使用案例
    func ExampleNumber_Multi() {
        num := Number{
            a: 2,
            b: 6,
        }
        fmt.Println(num.Multi())
    
        // Output:
        // 12
    }
    
    // 基准测试
    func BenchmarkNumber_Multi(b *testing.B) {
        b.ResetTimer()
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            num := &Number{
                a: 33,
                b: 20,
            }
            if num.Multi() != 660 {
                b.FailNow()
            }
        }
    }


    int 与 string 类型互相转换的基准测试

    // strconv_test.go
    package test
    
    import (
        "fmt"
        "strconv"
        "testing"
    )
    
    func BenchmarkSprintf(b *testing.B) {
        n := 10
        b.ResetTimer()
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            fmt.Sprintf("%d", n)
        }
    }
    
    func BenchmarkItoa(b *testing.B) {
        n := 10
        b.ResetTimer()
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            strconv.Itoa(n)
        }
    }
    
    func BenchmarkFormatInt(b *testing.B) {
        n := int64(10)
        b.ResetTimer()
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            strconv.FormatInt(n, 10)
        }
    }
    
    func BenchmarkAtoi(b *testing.B) {
        n := "10"
        b.ResetTimer()
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            strconv.Atoi(n)
        }
    }
    
    func BenchmarkParseInt(b *testing.B) {
        n := "10"
        b.ResetTimer()
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            strconv.ParseInt(n, 10, 0)
        }
        b.StopTimer()
    }

    测试结果

    # -4 表示使用 4 核心 | CPU 基准测试次数 | 单次测试使用时间 | 单次测试处理的字节数 | 总的分配内存的次数
    $ go test -bench=.
    goos: linux
    goarch: amd64
    pkg: learn/test
    BenchmarkSprintf-4       6880945               172 ns/op              16 B/op          2 allocs/op
    BenchmarkItoa-4         183639746                6.36 ns/op            0 B/op          0 allocs/op
    BenchmarkFormatInt-4    180059316                6.38 ns/op            0 B/op          0 allocs/op
    BenchmarkAtoi-4         100000000               10.3 ns/op             0 B/op          0 allocs/op
    BenchmarkParseInt-4     36338739                29.6 ns/op             0 B/op          0 allocs/op
    PASS
    ok      learn/test        8.164s


    += 与 buffer 字符串连接基准测试

    // 利用 += 连接
    func BenchmarkConcatStringByAdd(b *testing.B) {
        chars := []string{"1", "2", "3", "4", "5"}
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            ret := ""
            for _, char := range chars {
                ret += char
            }
        }
        b.StopTimer()
    }
    
    // 利用 buffer 连接
    func BenchmarkConcatStringBytesBuffer(b *testing.B) {
        chars := []string{"1", "2", "3", "4", "5"}
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            var buf bytes.Buffer
            for _, char := range chars {
                buf.WriteString(char)
            }
        }
        b.StopTimer()
    }

    测试结果

    # 使用 -benchmem 参数等同于在测试程序中调用 b.ReportAllocs()
    $ go test -bench=. -benchmem
    goos: linux
    goarch: amd64
    pkg: test
    BenchmarkConcatStringByAdd-4             4398699               278 ns/op              16 B/op          4 allocs/op
    BenchmarkConcatStringBytesBuffer-4       7655295               154 ns/op              64 B/op          1 allocs/op
    PASS
    ok      test    2.844s


    本文标题:golang 单元测试
    本文链接:https://lanseyujie.com/post/golang-unit-testing.html
    版权声明:本文使用「署名-非商业性使用-相同方式共享」创作共享协议,转载或使用请遵守署名协议。
    点赞 0 分享 0