golang 单元测试
单元测试介绍
为了保证代码的质量,很多公司都会要求写单元测试。这里介绍两个指标,
- 函数覆盖率:函数调用个数/函数个数,通常要求100%
- 行覆盖率:走到的行的个数/总函数,通常要求>60%
通过单元测试,我们可以针对不同场景进行测试,是研发自己对质量的把控。 笔者目前所在的公司对单元测试要求很高,并且有替代测试的趋势。
go test
- go的test一般以xxx_test.go为文件名,xxx并没有特别要求是要实测的文件名
- TestMain作为初始化test
- Testxxx(t* testing.T)
- go test 即可运行单元测试
- go test --v --test.run funcName 可以指定单测某个方法
创建一个client包进行示例,包结构如下
. ├── client.go ├── client_test.go 复制代码
func TestUser(t *testing.T) { var u = &User{Age: 15, Name: "alice"} if u.Age != 15 { t.Error("age err") } if u.Name != "bob" { t.Error("name err") } } 复制代码
由于Name不符合预期,则会有如下提示
--- FAIL: TestUser (0.00s) client_test.go:28: name err FAIL exit status 1 FAIL test/mocktest 0.005s 复制代码
go convey
goconvey可以很好的支持setup和teardown,goconvey可以在运行单个测试用例前都进行一次状态初始化和销毁。goconvey还有很多已经定义好了的能够直接使用的assert函数,并且可以自定义assert函数。 常用的assert如下:
var ( ShouldEqual = assertions.ShouldEqual ShouldNotEqual = assertions.ShouldNotEqual ShouldBeGreaterThan = assertions.ShouldBeGreaterThan ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo ShouldBeLessThan = assertions.ShouldBeLessThan ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo ShouldBeBetween = assertions.ShouldBeBetween ShouldNotBeBetween = assertions.ShouldNotBeBetween ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual ShouldContainSubstring = assertions.ShouldContainSubstring ShouldNotContainSubstring = assertions.ShouldNotContainSubstring ShouldPanic = assertions.ShouldPanic ShouldBeError = assertions.ShouldBeError ) 复制代码
使用举例:
func TestUser(t *testing.T) { convey.Convey("TestUser", t, func() { var u = &User{Age: 15, Name: "alice"} convey.So(u.Age, convey.ShouldEqual, 15) convey.So(u.Name, convey.ShouldEqual, "bob") }) } 复制代码
由于Name不符合预期,会出现如下提示
Line 30: Expected: 'bob' Actual: 'alice' (Should be equal) 复制代码
mock
什么是mock
单元测试的时候,如果流程中有第三方依赖怎么办?比如当贷款支付的时候,需要用户的额度,而额度信息存在于另一个微服务,需要rpc拉取。为了解决这种场景,我们可以使用mock这种方式。简单来说,mock就是能指定依赖接口的输入 输出,可以理解为提前插入的固定数据,如此,流程就能正常跑起来。
使用mockery进行mock
限制:
- 只能针对接口进行mock
使用流程
- 安装mockery:go get github.com/vektra/mockery/.../
- mockery -name=接口名,生成mocks目录。
rpc接口定义,接口实现
client.go:
package rpc //go:generate mockery -name=Client type Client interface { Get(key string) (data interface{}, err error) } type ClientImpl struct { Ct Client } func (p *ClientImpl) Get(key string) (data interface{}, err error) { if mockCondition { return p.Ct.Get(key) } // real logic } 复制代码
client_test.go
package rpc import ( "fmt" "test/mocktest/mocks" "testing" ) type User struct { Age int Name string } func TestMock(t *testing.T) { convey.Convey("TestMock", t, func() { mc := &mocks.Client{} var u = &User{Age: 15, Name: "alice"} mc.On("Get", "alice").Return(u, nil) ci.Ct = mc data, err := ci.Get("alice") convey.So(data.Age, convey.ShouldEqual, 15) }
有疑问加站长微信联系(非本文作者)