这是我参与「第五届青训营 」笔记创作活动的第4天
主要内容
- 单元测试
- Mock测试
- 基准测试
单元测试
输入测试单元得到输出,与期望值做一个校对
单元测试-规则
- 所有测试文件以_test.go结尾
- 函数命名为func TestXxx(*testing.T)
- 初始化逻辑放到函数TestMain中
单元测试-例子
```
package unit_testing
func HelloTom() string {//正确的情况应该是返回"Tom"
return "han"
}
```
测试文件
```
package unit_testing
import "testing"
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
if output != expectOutput {
t.Errorf("预期为%v,而不是%v", expectOutput, output)
}
}
```
go test运行得到结果:
=== RUN TestHelloTom
HelloTom_test.go:9: 预期为Tom,而不是han
--- FAIL: TestHelloTom (0.00s)
FAIL
Process finished with the exit code 1
单元测试-assert
前面我们在验证是否相等时使用的是"!="运算符,我们也可以使用一些包来比较
先导包(视频里没有),在终端运行: go get github.com/stretchr/testify/assert
修改后的代码
```
package unit_testing
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
//if output != expectOutput {
// t.Errorf("预期为%v,而不是%v", expectOutput, output)
//}
assert.Equal(t, expectOutput, output)
}
```
再次执行go test
PS D:\code\Golang\src\qxy\practice\unit_testing> go test
--- FAIL: TestHelloTom (0.00s)
HelloTom_test.go:14:
Error Trace: D:\code\Golang\src\qxy\practice\unit_testing\HelloTom_test.go:14
Error: Not equal:
expected: "Tom"
actual : "han"
Diff:
--- Expected
+++ Actual
@@ -1 +1 @@
-Tom
+han
Test: TestHelloTom
FAIL
exit status 1
FAIL qxy/practice/unit_testing 0.185s
PS D:\code\Golang\src\qxy\practice\unit_testing>
单元测试-覆盖率
用来衡量代码测试程度的指标
我们在终端执行: go test HelloTom_test.go HelloTom.go --cover 即可获得代码覆盖率
刚刚代码执行得到的覆盖率的结果:100%
我们尝试在HelloTom.go添加一个在测试中没有使用的函数,再次执行测试,得到覆盖率结果为:50%
单元测试-tips
- 一般覆盖率:50%-60%,较高覆盖率80%+
- 测试分支相对独立、全面覆盖
- 测试单元粒度足够小,函数单一原则
单元测试-依赖
外都依赖=>稳定&幂等
外部依赖包括文件、数据库、Cache等
幂等:指多次测试得到的结果应该是一致的
稳定:指单元测试间是相互隔离的,我们可以在任何时间去执行独立的测试单元
Mock
单元测试-Mock
在上面单元测试-依赖中,如果我们依赖的是外部文件,那么如果外部文件被修改,那么我们就无法进行正常的测试,这时我们就引入Mock,即打桩测试,原理是运行时将原函数的指针指向一个新的地址
以开源框架monkey为例(GitHub - bouk/monkey: Monkey patching in Go)
核心的函数Patch
```
// Patch replaces a function with another
func Patch(target, replacement interface{}) *PatchGuard {//这个函数会在运行时将target替换为replacement
t := reflect.ValueOf(target)
r := reflect.ValueOf(replacement)
patchValue(t, r)
return &PatchGuard{t, r}
}
```
当我们调用完Patch函数后,需要调用Unpatch函数来卸载包函数
```
// Unpatch removes any monkey patches on target
// returns whether target was patched in the first place
func Unpatch(target interface{}) bool {
return unpatchValue(reflect.ValueOf(target))
}
```
这样的测试可以在任何时间,任何环境测试,完全不依赖本地文件
基准测试
基准测试和单元测试差不多,测试的是程序运行时cpu的性能
- 函数命名为func BeachmarkXxx(*testing.B)