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

60 阅读2分钟

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

测试关系着系统的质量,质量则决定着线上系统的稳定性,一旦出现bug漏洞,就会造成事故。

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

测试一般分为:

  • 回归测试是QA同学手动通过终端回归一些固定的主流程场景;
  • 集成测试是对系统功能维度做测试验证;
  • 单元测试是测试开发阶段,开发者对单独的函数、模块做功能验证。
image.png

层级从上至下,测试成本逐渐减低,而测试覆盖率逐步上升,所以单元测试覆盖率一定程度上决定这代码的质量。

单元测试

测试规则:

  • 所有测试文件以_test.go结尾
  • func TestXxx(*testing.T){} 函数模板
  • 初始化逻辑放到TestMain中

代码覆盖率:

  • 一般覆盖率:50%~60%,较高覆盖率:80%+
  • 测试分支相互独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

Mock测试-解决测试中的依赖问题

工程中复杂的项目, -般会依赖****.* 而我们的单测需要保证稳定性和幂等性,稳定是指相互隔离,能在任何时间,任何环境,运行测试。幂等是指每一次测试运行都应该产生与之前一样的结果。而要实现这一目的就要用到mock机制。

func ReadFirstLine() string {
	open, err := os.Open("log") // 打开一个文件
	defer open.Close()
	if err != nil {
		return ""
	}
	scanner := bufio.NewScanner(open) // 对每行进行遍历
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

func ProcessFirstLine() string {
	line := ReadFirstLine()
	destLine := strings.ReplaceAll(line, "11", "00") // 替换11为00
	return destLine
}

func TestProcessFirstLine(t *testing.T) { // 执行单元测试
	firstLine := ProcessFirstLine()
	assert.Equal(t, "line00", firstLine)
}

若文件进行了修改,测试就寄了

快速Mock函数:

// 用函数A去替换函数B,B就是原函数,A就是打桩函数

func Patch(target, replacement interface{}) *PatchGuard {
    // target就是原函数,replacement就是打桩函数
	t := reflect.ValueOf(target)
	r := reflect.ValueOf(replacement)
	patchValue(t, r)
	return &PatchGuard{t, r}
}

func Unpatch(target interface{}) bool {
    // 保证了在测试结束之后需要把这个包卸载掉
	return unpatchValue(reflect.ValueOf(target))
}

func TestProcessFirstLineWithMock(t *testing.T) {
	monkey.Patch(ReadFirstLine, func() string {  
		return "line110"
	})
	defer monkey.Unpatch(ReadFirstLine)
	line := ProcessFirstLine()
	assert.Equal(t, "line000", line)
}
// 通过patch对ReadFirstLine进行打桩mock,默认返回line110,通过defer卸载mock
// 这样整个测试函数就摆脱了本地文件的束缚和依赖

基准测试

基准测试是指测试一段程序的性能及耗费CPU的程度;

在实际的项目开发中,经常会遇到代码性能瓶颈,为了定位问题,经常要对代码做性能分;

这时就用到了基准测试,其使用方法与单元测试类似。

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