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

95 阅读5分钟

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

01 本堂课重点内容

  • 单元测试
    • Mock测试
  • 基准测试

02 详细知识点介绍

  • 测试决定系统的质量,质量决定着稳定性与“生命”
  • 测试是避免事故的最后一道屏障

image.png

2.1 事故案例

  1. 营销配置错误,导致非预期用户享受权益,损失资金
  2. 幂等失效,用户短时间可以多次提现,损失资金
  3. 代码逻辑错误,广告位被占,损失收入

2.2 测试分类

  • 回归测试
    • QA(Quality Assurance)质量保证人员,手动通过终端回归一些固定的主流程场景
      • e.g.刷一下抖音,回复一下抖音的评论
    • 在缺陷修复之后的检验测试
  • 集成测试
    • 对系统功能维度做测试验证
    • 在单元测试的基础上将所有模块按照要求设计组装成为子系统或系统,进行集成测试
      • 测试接口,不同模块之间是否按照预期工作,比如不同模块之间的数据传输
  • 单元测试
    • 面对测试开发阶段,开发者对单独的函数、模块做功能验证
    • 是对软件中最小可测试单元(人为规定的最小必测功能模块)进行检查和验证
    • 软件的独立单元将在与程序的其他部分相隔离的情况下进行测试
  • 层级从上至下,测试成本逐渐减低,而测试覆盖率确逐步上升,所以单元测试的覆盖率一定程度上决定这代码的质量。

image.png

2.3 单元测试

image.png

  • 一方面可以保证质量,在整体覆盖率足够的情况下,一定程度上既保证了新功能本身的正确性又未破坏原有代码的正确性
  • 另一方面可以提升效率,在代码有bug的情况下,通过编写单测,可以在一个较短周期内定位和修复问题
  • 规则
    • 所有测试文件以_test.go结尾
    • 命名规范:大驼峰func TestPublishPost(t *testing.T)
    • 初始化逻辑放在TestMain
func TestMain(m *tesing.M){
    //测试前:数据装载、配置初始化等前置工作
    
    code := m.Run() //跑这个package下的所有单测
    
    //测试后:释放资源等收尾工作
    
    os.Exit(code)
}

  • 举例

image.png

image.png

  • assert
    • 实现Equal和NotEqual的比较

image.png

  • 代码覆盖率
    • 衡量代码是否经过了足够的测试
    • 评价项目测试水准
    • 评估项目是否达到了高水准测试的等级
    • 加上--cover参数
      • 下面第一个图例:被覆盖了函数的前两行
      • 下面第二个图例:提升覆盖率
    • Tips
      • 一般覆盖率:50% ~ 60%
        • 保证主功能没有问题
      • 较高覆盖率:80%+
        • 资金类交易等,有更高的覆盖率要求
        • 成本较大
      • 保证测试分支是完备的
        • 相互独立、全面覆盖
      • 保证测试单元粒度足够小
        • 与函数单一职责相对应

image.png

image.png

2.3.1 Mock机制

  • 依赖
  • 要保证稳定性与幂等性
    • 稳定是指相互隔离,能在任何时间,任何环境,运行测试
    • 幂等是指每一次测试运行都应该产生与之前一样的结果
      • 而要实现这一目的就要用到Mock机制

image.png

  • mock(常指为取笑而)模仿;(UK)模拟考试
    • 在软件测试中,mock所模拟的对象是SUT 的依赖(dependency)
    • 换而言之,mock 的作用是模拟 SUT 依赖对象的行为
    • 测试的对象一般称之为SUT(Software Under Test)(见下图)
    • 为什么需要模拟 B 的行为呢?
      • 提高 A 的测试覆盖率
        • A 依赖 B,本质上依赖的是 B 的返回结果,也就是说 B 的返回结果会影响 A 的行为。通过 mock B 我们可以构造各种正常和异常的来自 B 的返回结果,从而更充分测试 A 的行为
      • 避免 B 的因素从而对 A 产生影响,依赖真实的 B 去测试 A 可能有很多问题:
        • B 的开发没有完成时无法测试 A;
        • B 有阻塞性bug 时无法测试 A;
        • B 的依赖 C 有阻塞性 bug 时无法测试 A;
      • 提高 A 的测试效率
        • B 的真实行为可能很慢,而 B 的模拟行为是非常快的,因此可以加快 A 的测试执行速度。

image.png

  • mock 种族
    • 方法级别 mock
      • mock 的对象是一个函数调用,例如获取系统环境变量。
    • 类级别 mock
      • mock 的对象是一个类,例如一个 HTTP server。
    • 接口级别 mock
      • mock 的对象是一个 API 接口。
    • 服务级别 mock
      • mock 的对象是整个服务。比如前端工程师自测试时,可以讲后端整个服务都 mock 掉,这其实等同于将后端的所有接口都 mock。

image.png

  • mock 的本质就是用模拟桩来替换真实的依赖
    • 所谓 mock 桩注入就是阻断被测服务与真实服务之间的链路,建立被测服务与 mock 之间的链路过程

image.png

  • 如何注入 mock?

    • API 请求构造image.png
    • 本地配置image.png
    • 配置中心image.png
    • 反向代理
    • 前向代理
  • Patch

    • target 原函数
    • replacement 需要打桩的函数
  • Unpatch 保证测试结束后把桩卸载

image.png

image.png

2.4 基准测试

  • 基准测试
    • 指测试一段程序的运行性能及耗费 CPU 的程度
    • 在实际项目开发中,经常会遇到代码性能瓶颈,为了定位问题经常要对代码做性能分析,这就用到了基准测试
    • 使用方法类似于单元测试
    • 服务器负载均衡的例子
      • 有10个服务器列表,每次随机执行select函数随机选择一个执行image.png
      • 基准测试以Benchmark开头,入参是 *testing.B , ResetTimer重置定时器,用b中的N值反复递增循环,做串行的压力测试,RunParallel做多协程并行压力测试image.png
        • 发现代码在并发情况下存在劣化,主要原因是rand为了保证全局的随机性和并发安全,持有了一把全局锁
        • 可以使用fastrand进行优化

03 引用参考 & 文章收藏