Go语言中测试固件

测试固件(test fixture)一般指的是一个人造的、确定性的环境。一个测试用例或一个测试套件在这个环境中运行,其测试结果是可重复的。一般用setUp和tearDown来代表测试固件的创建/设置与拆除/销毁动作。

一般的做法是在setUp中返回匿名函数来实现tearDown。这样做的好处是可以在setUp中利用闭包特性在两个函数间共享一些变量,避免了包级别变量的使用。

func setUp(testName string) func() {
	... // 创建测试固件
	return func() { // 测试固件销毁函数
		... // 销毁测试固件
	}
}

来看看具体使用方法:

import "testing"

func setUp(testName string) func() {
	println("setUp fixture")
	return func() {
		println("tearDown fixture")
	}
}

func TestFunc1(t *testing.T) {
	defer setUp(t.Name())()
	println("testing func1")
}

func TestFunc2(t *testing.T) {
	defer setUp(t.Name())()
	println("testing func2")
}

$ go test -v .
=== RUN   TestFunc1
setUp fixture
testing func1
tearDown fixture
--- PASS: TestFunc1 (0.00s)
=== RUN   TestFunc2
setUp fixture
testing func2
tearDown fixture
--- PASS: TestFunc2 (0.00s)
PASS
ok      github.com/demo/test-demo       0.581s

上例中运行每个测试函数TestXxx时,都会先通过setUp函数创建测试固件,并在defer函数中注册测试固件销毁的函数(defer关键字后表达式求值顺序:https://www.nowcoder.com/discuss/468918522509336576),以保证每个TestXxx执行完毕后为其建立的测试固件被销毁,使得各个测试函数之间的测试执行互不干扰。

在Go 1.14版本testing包增加了testing.Cleanup方法,为测试固件的销毁提供了包级原生支持。

func setUp(testName string) func() {
	println("setUp fixture")
	return func() {
		println("tearDown fixture")
	}
}

func TestFunc3(t *testing.T) {
	t.Cleanup(setUp(t.Name()))
	println("testing func3")
}

$ go test -v .
=== RUN   TestFunc3
setUp fixture
testing func3
tearDown fixture
--- PASS: TestFunc3 (0.00s)
PASS
ok      github.com/demo/test-demo       0.610s

有些时候需要将所有测试函数放入一个更大范围的测试固件环境中执行——包级别测试固件。Go 1.14版本中引入了TestMain,使得包级别测试固件的创建和销毁更容易:

import (
	"fmt"
	"testing"
)

func setUp(testName string) func() {
	println("setUp fixture")
	return func() {
		println("tearDown fixture")
	}
}

func TestFunc1(t *testing.T) {
	t.Cleanup(setUp(t.Name()))
	fmt.Printf("testing %s\n", t.Name())
}

func TestFunc2(t *testing.T) {
	t.Cleanup(setUp(t.Name()))
	fmt.Printf("testing %s\n", t.Name())
}

func TestFunc3(t *testing.T) {
	t.Cleanup(setUp(t.Name()))
	fmt.Printf("testing %s\n", t.Name())
}

func pkgSetUp(testName string) func() {
	println("package setUp fixture")
	return func() {
		println("package tearDown fixture")
	}
}

func TestMain(m *testing.M) {
	defer pkgSetUp("package test")
	m.Run()
}

$ go test -v .
package setUp fixture
=== RUN   TestFunc1
setUp fixture
testing TestFunc1
tearDown fixture
--- PASS: TestFunc1 (0.00s)
=== RUN   TestFunc2
setUp fixture
testing TestFunc2
tearDown fixture
--- PASS: TestFunc2 (0.00s)
=== RUN   TestFunc3
setUp fixture
testing TestFunc3
tearDown fixture
--- PASS: TestFunc3 (0.00s)
PASS
package tearDown fixture
ok      github.com/demo/test-demo       0.596s

从上例中可以看到,包级别测试固件在所有测试函数执行之前就被创建了,在包级别测试函数执行完成之后被销毁。注意:对于包级别测试固件的销毁,没有Cleanup方法,我们依然利用defer的特性实现测试固件的销毁。

如果一些测试函数所需要的测试固件是相同的,可以使用测试套件的方式来减少测试固件的重复创建:

import (
	"fmt"
	"testing"
)

func suiteSetUp(testName string) func() {
	fmt.Printf("setUp fixture for %s\n", testName)
	return func() {
		fmt.Printf("tearDown fixture for %s\n", testName)
	}
}

func func1TestCase1(t *testing.T) {
	fmt.Printf("testing %s\n", t.Name())
}

func func1TestCase2(t *testing.T) {
	fmt.Printf("test %s\n", t.Name())
}

func func2TestCase1(t *testing.T) {
	fmt.Printf("testing %s\n", t.Name())
}

func func2TestCase2(t *testing.T) {
	fmt.Printf("testing %s\n", t.Name())
}

func TestFunc1(t *testing.T) {
	t.Cleanup(suiteSetUp(t.Name()))
	t.Run("testcase1", func1TestCase1)
	t.Run("testcase2", func1TestCase2)
}

func TestFunc2(t *testing.T) {
	t.Cleanup(suiteSetUp(t.Name()))
	t.Run("testcase1", func2TestCase1)
	t.Run("testcase1", func2TestCase2)
}

func pkgSetUp(pkgName string) func() {
	fmt.Printf("setUp package fixture for %s\n", pkgName)
	return func() {
		fmt.Printf("teatDown package fixture for %s\n", pkgName)
	}
}

func TestMain(m *testing.M) {
	defer pkgSetUp("package test")()
	m.Run()
}

$ go test -v .
setUp package fixture for package test
=== RUN   TestFunc1
setUp fixture for TestFunc1
=== RUN   TestFunc1/testcase1
testing TestFunc1/testcase1
=== RUN   TestFunc1/testcase2
test TestFunc1/testcase2
tearDown fixture for TestFunc1
--- PASS: TestFunc1 (0.00s)
    --- PASS: TestFunc1/testcase1 (0.00s)
    --- PASS: TestFunc1/testcase2 (0.00s)
=== RUN   TestFunc2
setUp fixture for TestFunc2
=== RUN   TestFunc2/testcase1
testing TestFunc2/testcase1
=== RUN   TestFunc2/testcase1#01
testing TestFunc2/testcase1#01
tearDown fixture for TestFunc2
--- PASS: TestFunc2 (0.00s)
    --- PASS: TestFunc2/testcase1 (0.00s)
    --- PASS: TestFunc2/testcase1#01 (0.00s)
PASS
teatDown package fixture for package test
ok      github.com/demo/test-demo       0.601s

全部评论

相关推荐

11-09 10:42
已编辑
海南大学 算法工程师
明阳智能 软件开发岗 总包19
点赞 评论 收藏
分享
1 1 评论
分享
牛客网
牛客企业服务