手把手教学-Go工程实践之测试 | 青训营

80 阅读5分钟

Go工程实践之测试

不是开玩笑的,本文只要你愿意跟着抄,基本就能把课上的东西弄明白抄一遍🐶,实操写的非常非常详细。如果你看不明白,到下方github仓库提交issue,我来帮你解决问题🕊

如果这篇文章对你有帮助,希望你能留下点赞,如果你能到github点个star就更好了(@^0^@)/。

本文所有案例代码都可以在这里获取: MoFishXiaodui/ExecutableManual: 青训营后端-可执行手册-非常详细的手把手教学教程 (github.com)

相关资料

Manual

此次可操作手册以PPT顺序为主,建议大家对照着PPT一起阅读,并且跟着操作

03 测试

基础测试的章节在手把手教学-Go语言进阶与依赖管理课 | 青训营 - 掘金 (juejin.cn)后半部分,未学习的读者需要自行往回翻阅

案例10 - 依赖文件的单元测试

  1. 新文件夹 2-2-10file,在内新建file.gofile_test.golog.txt 文件

  2. log.txt

    line11
    line22
    line33
    
  3. file.go

    package main
    ​
    import (
        "bufio"
        "fmt"
        "os"
        "strings"
    )
    ​
    func ReadFirstLine() string {
        file, err := os.Open("log.txt")
        defer file.Close()
        if err != nil {
            return ""
        }
        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            return scanner.Text()
        }
        return ""
    }
    ​
    func ProcessFirstLine() string {
        line := ReadFirstLine()
        destLine := strings.ReplaceAll(line, "11", "00")
        return destLine
    }
    ​
    func main() {
        line := ProcessFirstLine()
        fmt.Println(line)
    }
    
  4. file_test.go

    package main
    ​
    import (
        "github.com/stretchr/testify/assert"
        "testing"
    )
    ​
    func TestProcessFirstLine(t *testing.T) {
        firstLine := ProcessFirstLine()
        assert.Equal(t, "line00", firstLine)
    }
    
  5. 先运行 go run file.go查看效果

    src\2-2-10file> go run .\file.go
    line00
    
  6. 再运行 go test file.go file_test.go 查看效果

    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-10file> go test .\file.go .\file_test.go
    ok      command-line-arguments  0.213s
    ​
    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-10file> go test .\file.go .\file_test.go --cover
    ok      command-line-arguments  0.227s  coverage: 69.2% of statements
    
  7. 总结

    1. ReadFirstLine 函数可以读取 log.txt 文件并返回第一行
    2. ProcessFirstLine 函数可以调用 ReadFirstLine 函数获取第一行文本副本,并对该副本做加工,把"11"替换成"00"
    3. TestProcessFirstLine 函数用来测试 ProcessFirstLine 函数是否正确
    4. ReadFirstLine 函数的内容返回依赖于文件log.txt,如果log.txt被删除,就不能测试ProcessFirstLine的正确性。接下来的案例我们将设法替换 ReadFirstLine函数,使测试不依赖于 log.txt文件

案例11 - mock

mock

  • 为一个函数打桩
  • 为一个方法打桩

此次mock需要用到"bou.ke/monkey"包

  1. 复制案例10的整个文件夹,命名为:2-2-11mock,删掉log.txt文件。(把ReadFirstLine的依赖删除)

  2. 添加monkey包,在终端运行go get "bou.ke/monkey"

    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-11mock> go get "bou.ke/monkey"
    go: added bou.ke/monkey v1.0.2
    
  3. 修改file_test.go文件的测试函数代码。(就是在中间多了一个函数替换操作,也就是打桩)

    // 原来
    func TestProcessFirstLine(t *testing.T) {
        firstLine := ProcessFirstLine()
        assert.Equal(t, "line00", firstLine)
    }
    ​
    // 现在
    func TestProcessFirstLine(t *testing.T) {
        monkey.Patch(ReadFirstLine, func() string {
            return "line11"
        })
        defer monkey.Unpatch(ReadFirstLine)
    ​
        firstLine := ProcessFirstLine()
        assert.Equal(t, "line00", firstLine)
    }
    
  4. 最终代码见src/2-2-11mock/

  5. 案例10一样测试

    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-11mock> go test .\file.go .\file_test.go --cover 
    ok      command-line-arguments  0.218s  coverage: 23.1% of statements
    
  6. 总结:mock可以通过替换函数的方式,从而可以删去部分依赖。

案例12 - 基准测试

  1. 新建文件夹 2-2-12bench ,在内新建文件 server.goserver_test.go

  2. 初始化,在 2-2-12bench 目录下执行 go mod init server

  3. server.go中写入代码

    package bench
    ​
    import "math/rand"var ServerIndex [10]int// 初始化服务函数,(其实就是往数组里面各个项写入特定数字,i+100本身应该没什么含义)
    func InitServerIndex() {
        for i := 0; i < 10; i++ {
            ServerIndex[i] = i + 100
        }
    }
    ​
    func Select() int {
        // 随意选择一个服务器(随机选择一个数组元素,模拟随机一个服务器返回数字)
        return ServerIndex[rand.Intn(10)]
    }
    
  4. server_test.go 中写入代码

    package bench
    ​
    import "testing"
    ​
    func BenchmarkSelect(b *testing.B) {
        InitServerIndex()
        b.ResetTimer()
    ​
        for i := 0; i < b.N; i++ {
            Select()
        }
    }
    ​
    // parallel 平行的,同时发生的
    func BenchmarkSelectParallel(b *testing.B) {
        InitServerIndex()
        b.ResetTimer()
        b.RunParallel(func(pb *testing.PB) {
            for pb.Next() {
                Select()
            }
        })
    }
    
  5. 最终代码见src/2-2-12bench/

  6. 运行命令 go test -bench=Ben,查看效果。指令中Ben是指以Ben开头的测试函数都执行

    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-12bench> go test -bench=B
    goos: windows
    goarch: amd64
    pkg: server
    cpu: 12th Gen Intel(R) Core(TM) i5-12490F
    BenchmarkSelect-12              71638796                15.41 ns/op
    BenchmarkSelectParallel-12      28514331                42.61 ns/op
    PASS
    ok      server  2.562s
    ​
    # 只测并行
    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-12bench> go test -bench=BenchmarkSelectP
    goos: windows
    goarch: amd64
    pkg: server
    cpu: 12th Gen Intel(R) Core(TM) i5-12490F
    BenchmarkSelectParallel-12      27168068                43.41 ns/op
    PASS
    ok      server  1.399s
    

案例13 - 基准测试优化

  1. 复制案例122-2-12bench 文件夹,重命名为 2-2-13benchOptimize

  2. 2-2-13bencOptimize文件夹中执行命令, go get "github.com/bytedance/gopkg@main" 添加字节的gopkg

    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-13benchOptimize> go get "github.com/bytedance/gopkg@main"
    go: downloading github.com/bytedance/gopkg v0.0.0-20220118071334-3db87571198b
    go: added github.com/bytedance/gopkg v0.0.0-20220118071334-3db87571198b
    
  3. 在文件 server.go中导入字节的fastrand包

    import "github.com/bytedance/gopkg/lang/fastrand"
    

    这里说一下是怎么知道在路径 gopkg/lang/fastrand

    方法一:

    • 当你执行go get 命令后,你可以去 $GOPATH/pkg/mod找到你下载的包,然后在里面查找就能找到fastrand包的位置
    • 如果你不知道你的 $GOPATH在哪,你可以在终端输入go env 确定

    方法二(其实我也是用方法二的时候才确定用方法一再找一遍的):

  4. 修改下方的随机选择服务器代码中的随机函数为快速随机函数

    // 原来
    return ServerIndex[rand.Intn(10)]
    ​
    // 现在
    return ServerIndex[fastrand.Intn(10)]
    
  5. 案例12一样进行测试

    (base) PS D:\code\MoFishXiaodui\ExecutableManual\src\2-2-13benchOptimize> go test -bench=B
    goos: windows
    goarch: amd64
    pkg: server
    cpu: 12th Gen Intel(R) Core(TM) i5-12490F
    BenchmarkSelect-12              523615494                2.292 ns/op
    BenchmarkSelectParallel-12      1000000000               0.3863 ns/op
    PASS
    ok      server  2.039s
    
  6. 可以发现这比案例12要快很多