Golang 业务代码单元测试实践
背景 什么是单测 单元测试是一项由开发人员或测试人员对程序模块的正确性进行检验测试的工作,用于检查被测试代码的功能是否正确。单元测试是软件工程中降低开发成本,提高软件质量常用方式之一。 正确性:同一单元,在给定的输入下,总能得到预期的输出。 为什么要写单测 在业务快速迭代过程中,单元测试能够保证业务逻辑的一致性。(如修改业务逻辑、或者引入缺陷导致业务逻辑变更,单测会发现问题并报错,前提是测试用例能够覆盖到改动的场景) 利于未来的代码重构,保证基本的业务功能不变,不会出现严重的基本功能不可用的情况。 复杂的代码块单测十分复杂,有助于开发者将复杂逻辑拆分为多个内聚的小模块,降低了代码耦合度,提升了代码质量,内聚的小模块也更方便测试。 整体单测覆盖率达到一定程度后,能够保证项目的基本质量。 更有信心修改代码,自动化的单测能够确保基本业务逻辑没有问题,还能节省开发者自测的时间。 推动测试驱动开发(TDD,Test-Driven Development),让开发者在开发前优先设计接口,考虑各种输入输出场景,提升代码质量,覆盖更多边界用例。 …… 为什么都不想写单测 需要花费更多时间写单测,修改一行代码,要写300行单测。(从👆🏻能看出来,在未来可以节省很多时间,还能提升质量,减少故障) 代码太复杂,不够内聚,一个方法几百行,依赖了各种模块,调用满天飞。(需要重构代码,拆分为内聚的模块,不仅方便写单测,还能提升代码质量,方便未来迭代) 写单测很复杂,不清楚怎么写好单测。(模块设计合理的前提下,大部分单测都不复杂,👇🏻也会有一些最佳实践) 什么是好的单测实践 简短、粒度小:只有一个测试目的,不应该包含过多计算逻辑,尽量只有输入、输出和断言。 独立性:保证单元测试稳定可靠且便于维护,单测用例之间不能互相调用,也不能依赖执行的先后次序。 可重复:单元测试通常会被放到持续集成中,每次有集成时单元测试都会被执行。如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续集成机制的不可用。 自动化:单元测试应该是全自动执行的,并且非交互式的。 实践 Go Mockito 使用字节开源的 mockey 库可以很方便的实现函数 mock,解耦依赖调用,专注测试方法内部的业务逻辑。 Mockey 是一款简单易用的 Golang 打桩工具库,能够快速方便地进行函数、变量的 mock,目前在字节跳动各业务的单元测试编写中应用较为广泛,其底层是通过**运行时改写函数指令**实现的猴子补丁(Monkey Patch)。 // 示例代码 package main import ( "syscall" "unsafe" ) func a() int { return 1 } func b() int { return 2 } func rawMemoryAccess(b uintptr) []byte { return (*(*[0xFF]byte)(unsafe.Pointer(b)))[:] } func assembleJump(f func() int) []byte { funcVal := *(*uintptr)(unsafe....