测试是避免事故的最后一道屏障。
测试一般分为三种:
- 回归测试:一般是QA同学手动通过终端回归一些固定的主流程场景
- 集成测试:对系统功能维度做测试验证
- 单元测试:开发者对单独的函数、模块做功能验证
层级从上至下,测试成本逐渐减低,而测试覆盖率确逐步上升,所以单元测试的覆盖率一定程度上决定这代码的质量。
单元测试
单元测试一方面可以保证质量,在整体覆盖率足够的情况下,一定程度上既保证了新功能本身的正确性,又未破坏原有代码的正确性;另一方面可以提升效率,在代码有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 的程度。