Go-知识测试-子测试
Go-知识测试-子测试
- 1. 介绍
- 2. 例子
- 3. 子测试命名规则
- 4. 选择性执行
- 5. 子测试并发
- 6. testing.T.Run
- 7. testing.T.Parallel
- 8. 子测试适用于单元测试
- 9. 子测试适用于性能测试
- 10. 总结
- 10.1 启动子测试 Run
- 10.2 启动并发测试 Parallel
建议先看:https://blog.csdn.net/a18792721831/article/details/140062769
Go-知识测试-工作机制
1. 介绍
子测试提供了一种在一个测试函数中执行多个测试的能力,可以自己编排执行顺序,或者做一些初始化等操作。
2. 例子
比如如下代码:
import "testing" func SubA(t *testing.T) { t.Log("test A " + t.Name()) } func SubB(t *testing.T) { t.Log("test B " + t.Name()) } func SubC(t *testing.T) { t.Log("test C " + t.Name()) } func TestSub(t *testing.T) { t.Run("name=A", SubA) t.Run("name=B", SubB) t.Run("name=C", SubC) }
使用go test -v xx_test.go执行
3. 子测试命名规则
Run()方法的第一个参数作为子测试的名字,这里只是本层级的名字。
实际上子测试的内部命名规则是: /
比如我们在B里面在调用A:
执行后:
name具有传递性
4. 选择性执行
在上面我们有 name=A,name=B,name=C 三个测试,如果只是想执行A呢
使用go test -v xx_test.go -run TestSub/name=A
选择性执行的字符串,是包含匹配,不是严格的正则匹配。
5. 子测试并发
可以看到,前面的子测试都是根据代码顺序执行,串行执行,并没有并发。
使用 t.Parallel 可以让当前测试函数允许并发。
比如如下例子不加并发的时候:
import ( "testing" "time" ) func SubA(t *testing.T) { t.Log("test A " + t.Name()) time.Sleep(time.Second*3) } func SubB(t *testing.T) { t.Log("test B " + t.Name()) time.Sleep(time.Second*2) } func SubC(t *testing.T) { time.Sleep(time.Second*1) t.Log("test C " + t.Name()) } func TestSub(t *testing.T) { t.Run("name=A", SubA) t.Run("name=B", SubB) t.Run("name=C", SubC) }
执行后用时超过6秒
允许并发:
import ( "testing" "time" ) func SubA(t *testing.T) { t.Parallel() t.Log("test A " + t.Name()) time.Sleep(time.Second * 3) } func SubB(t *testing.T) { t.Parallel() t.Log("test B " + t.Name()) time.Sleep(time.Second * 2) } func SubC(t *testing.T) { t.Parallel() time.Sleep(time.Second * 1) t.Log("test C " + t.Name()) } func TestSub(t *testing.T) { t.Run("name=A", SubA) t.Run("name=B", SubB) t.Run("name=C", SubC) }
A最先被调度,但是确是最后执行完毕。
需要注意的是,当开启子测试并发后,调用了子测试后,父测试的代码和子测试代码执行顺序就是随机的了。
所以如果在主测试中需要做一些初始化,然后在执行子测试,那么需要注意,有可能初始化还未完成,子测试已经开始执行了。
6. testing.T.Run
// 将运行f作为名为name的t的子测试。它在一个单独的goroutine中运行f // 并且阻塞直到f返回或调用t。并行成为并行测试。 // 运行报告f是否成功(或者至少在调用t.Parallel之前没有失败)。 // // Run可以从多个goroutine同时调用,但所有此类调用 // 必须在t的外部测试函数返回之前返回。 func (t *T) Run(name string, f func(t *T)) bool { atomic.StoreInt32(&t.hasSub, 1) testName, ok, _ := t.context.match.fullName(&t.common, name) if !ok || shouldFailFast() { return true } //记录此调用点的堆栈跟踪,以便如果子测试 //在单独的堆栈中运行的函数被标记为助手,我们可以 //继续将堆栈遍历到父测试中。 var pc [maxStackLen]uintptr n := runtime.Callers(2, pc[:]) t = &T{ common: common{ barrier: make(chan bool), signal: make(chan bool, 1), name: testName, parent: &t.common, level: t.level + 1, creator: pc[:n], chatty: t.chatty, }, context: t.context, } t.w = indenter{&t.common} if t.chatty != nil { t.chatty.Updatef(t.name, "=== RUN %s\n", t.name) } //而不是在调用之前减少此测试的运行计数 //tRunner并在之后增加它,我们依靠tRunner保持 //计数正确。这样可以确保运行一系列顺序测试 //而不会被抢占,即使它们的父级是并行测试。这 //如果*parallel==1,则可以特别减少意外。 go tRunner(t, f) if !
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。