Golang 單元測試與 Mock
【導讀】go 項目中的單元測試和單元測試中的 mock 怎麼做?本文由騰訊工程師介紹 golang 單元測試和Mock 的實踐。
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)
}
}
轉自:牛牛碼特
juejin.cn/post/6844903921442390023
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/KgkeHxsUxg9fYSCuetPx9Q