Go 的测试 | 青训营

127 阅读2分钟

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

测试一般分为三种:

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

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

单元测试

image.png

单元测试一方面可以保证质量,在整体覆盖率足够的情况下,一定程度上既保证了新功能本身的正确性,又未破坏原有代码的正确性;另一方面可以提升效率,在代码有bug的情况下,通过编写单元测试,可以在一个较短周期内定位和修复问题。

规则

.
|-- publish_post.go
|-- publish_post_test.go
  • 所有测试文件以_test.go结尾,这样从文件上就很好了区分源码和测试代码

在测试文件中:

func TestPublishPost(t *testing.T) {
	// ...
}
  • func TestXxx(t *testing.T)
func TestMain(m *testing.M) {
    // 测试前:数据装载、配置初始化
    code := m.Run()
    //测试后:释放资源等收尾工作
    os.Exit(code)
}
  • 初始化逻辑放到TestMain

一个例子

// hello.go
package hello

func HelloTom() string {
	return "Jerry"
}

// hello_test.go
package hello

import "testing"

func TestHelloTom(t *testing.T) {
	output := HelloTom()
	if output != "Tom" {
		t.Errorf("HelloTom() = %s; want Tom", output)
	}
}

// go test Result
=== RUN   TestHelloTom
    d:\go_lang\test_example\hello\hello_test.go:8: HelloTom() = Jerry; want Tom
--- FAIL: TestHelloTom (0.00s)
FAIL
FAIL    hello/hello     0.464s

断言 assert

// hello.go
package hello

func HelloTom() string {
	return "Tom"
}

// hello_test.go
package hello

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

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


// go test Result
=== RUN   TestHelloTom
--- PASS: TestHelloTom (0.00s)
PASS
ok      hello/hello     3.042s

覆盖率

从代码层面度量测试执行范围的指标。在实际项目中,一般的要求是50%~60%覆盖率,而对于资金型服务,覆盖率可能要求达到80%。做单元测试时,测试分支相互独立、全面覆盖,这就要求函数体足够小,能够比较简单的提升覆盖率,也符合函数设计的单一职责。

Mock测试

工程中复杂的项目,一般会依赖文件、数据库、缓存等等,而我们的单元测试需要保证稳定性和幂等性。

稳定是指相互隔离,能在任何时间、任何环境运行测试;幂等是指每一次测试运行都应该产生与之前一样的结果。而要实现这一目的就要用到Mock机制。

Mock 测试就是在测试过程中,对于某些不容易构造或者不容易获取比较复杂的对象,用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

基准测试

Go 语言还提供了基准测试框架,基准测试是指测试一段程序的运行性能及耗费 CPU 的程度。