单元测试 | 青训营笔记

83 阅读3分钟

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

下面相关内容仅供本人回顾使用,如有问题,烦请评论反馈,感激不尽!!!

单元测试的质量某种程度上决定了代码的质量。

image.png

单元测试规则

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

image.png

单元测试——assert

import(
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestHelloTom(t *testing.T) {
    output := HelloTom()
    expectOutput := "Tom"
    assert.Equal(t, expectOutput, output)
}

func HelloTom() string {
    return "Tom"
}

单元测试——测试覆盖率

  • 如何衡量代码是否经过了足够的测试
  • 如何评价项目的测试水准
  • 如何评估项目是否达到了高水准测试等级
func JudgePassLine(score int16) bool {
    if score >= 60 {
        return true
    }
    else {
        return false
    }
}

func TestJudgePassLineTrue(t *testing.T) {
    isPass := JudgeePassLine(70)
    assert.Equal(t, true, isPass)
}

func TestJudgePassLineFalse(t *testing.T) {
    isPass := JudgeePassLine(50)
    assert.Equal(t, false, isPass)
}
  • 一般覆盖率:50%~60%,较高覆盖率:80%+
  • 测试分支相互独立、全面覆盖
  • 测试单元粒度足够小,函数单一职责

单元测试——依赖

image.png

  • 幂等:重复运行同一个case,结果与之前一致
  • 稳定:指单元测试相互隔离,可以独立运行

单元测试——文件处理

如果测试文件被修改了,可能就无法正确测试,这时候就需要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)
}
monkey: https://github.com/bouk/monkey
这是一个开源的mock测试库,可以对method或者实例的方法进行mock

Monkey Patch的作用域在Runtime,

运行时通过Go的unsafe包能够将内存中函数的地址替换为运行时函数的地址,将待打桩函数或方法的实现跳转。

快速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的程度;

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

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

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