Go语言工程实践之测试|青训营笔记

143 阅读3分钟

Go语言工程实践之测试|青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

1. 单元测试

测试是避免事故的最后一道屏障

  1. 回归测试
  2. 集成测试
  3. 单元测试

从上到下,覆盖率逐层变大,成本却逐层降低

测试单元:函数,模块......

把输出和期望进行校对,保证代码质量和提升效率

1.1 单元测试-规则

所有测试文件以_test.go结尾

*func TestXxxx(testing.T)

*初始化逻辑放到TestMain(m testing.M) 中

func TestMain(m *testing.M){
    //测试前:数据装载、配置初始化等前置工作
    code:=m.Run()
    //测试后:释放资源等收尾工作
    os.Exit(code)
}

1.2 单元测试-例子

func HelloTom() string {
    return "Jerry"
}
func TestHelloTom(t *testing.T){
    output:=HelloTom()
    expectOutput:="Tom"
    if output != expectOuput{
        t.Error("Expected %s do not not match actual %s",expectOutput,output)
    }
}

1.3 单元测试-运行

go test [flags] [packages]

1.4 单元测试-assert

修改上述错误函数继续测试

assert.Equal(t,expectOutput,output)

1.5 单元测试-覆盖率

代码执行的覆盖率,提升单测的覆盖度

一般覆盖率:50%~60%,较高覆盖率80%+。

测试分支相互独立、全面覆盖

测试单元粒度足够小,函数单一职责

1.6 单元测试-依赖

幂等,稳定性

Mock

外部依赖=>稳定&幂等

1.7 单元测试-文件处理

一旦文件篡改,测试在特定场景下不可运行

1.8 单元测试-Mock

开源Mock测试包

monkey: github.com/bouk/monkey

快速Mock函数

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

用一个函数A去替换另一个函数B,B是原函数,A是打桩函数

2. 基准测试

  • 优化代码,需要对当前代码分析
  • 内置的测试框架提供了基准测试的能力

2.1 基准测试-运行

串行和并行压力测试

func BenchmarkSelect(b *testing.B) {
    InitServerIndex()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        Select()
    }
}
func BenchmarkSelectParallel(b *testing.B) {
    InitServerIndex()
    b.ResetTimer()
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            Select()
        }
    })
}
func Select() int {
    return ServerIndex[rand.Intn(10)]
}

Select用到了rand函数,rand函数为了保证全局性和并发安全持有全局锁

2.2 基准测试-优化

fastrand() 地址:github.com/bytedance/g…

func FastSelect() int {
    return ServerIndex[fastrand.Intn(10)]
}

3. 项目实践

3.1 需求描述

社区话题页面

  • 展示话题(标题,文字描述)和回帖列表
  • 暂不考虑前端页面实现,仅仅实现一个本地Web服务
  • 话题和回帖数据用文件存储

3.2 需求用例

浏览消费用户

Topic

PostList

3.3 ER图

话题-帖子

1对多

Topic

  • id
  • title
  • content
  • create_time

Post

  • id
  • topic_id
  • content
  • create_time

3.4 分层结构

  • 数据层:数据Model,外部数据的增删改查
  • 逻辑层:业务Entity,处理核心业务逻辑输出
  • 视图层:视图view,处理和外部的交互逻辑

image-20230118185128880.png 数据层会屏蔽下游的数据差距,Service层不需要关心具体底层的数据存储,Service层主要通过接受Repository的数据做打包封装,输出实体,对应此需求就是话题页面,Controller层包装一些数据格式,

不同项目根据不同情况进行拆分,大致思路如此

3.5 组建工具

Gin 高性能go web框架

github.com/gin-gonic/g…

Go Mod

go mod init

go get gopkg.in/gin-gonic/gin.v1@v1.3.0

3.6 Repository-index

索引

var (
    postDao *PostDao
    postOnce sync.Once
)

sync.Once

单例模式减少存储的浪费

3.7 Service

流程

  • 参数校验->准备数据->组装实体

3.8 Controller

构建View对象

业务错误码

3.9 Router

  • 初始化数据索引
  • 初始化引擎配置
  • 构建路由
  • 启动服务