網站首頁 編程語言 正文
Go 單元測試工具
測試分為4個層次
- 單元測試:對代碼進行測試
- 集成測試:對一個服務的接口測試
- 端到端測試(鏈路測試):從一個鏈路的入口輸入測試用例,驗證輸出的系統的結果
- UI測試
常犯的錯誤:
- 沒有斷言。沒有斷言的單測是沒有靈魂的。
單測的特征:
- A:(Automatic,自動化):單元測試應該是全自動執行的,并且非交互式的
- I:(Independent,獨立性):為了保證單元測試穩定可靠且便于維護,單元測試用例之間決不能互相調用,也不能依賴執行的先后次序。
- R:(Repeatable,可重復):單元測試通常會被放到持續集成中,每次有代碼 check in 時單元測試都會被執行。
單測
代碼 bug 總是在所難免, 越早發現問題解決成本越低, 單測可以盡早的暴露錯誤。提高代碼之路,使得項目更高質量的交付。 起碼有三個優點:
- 提高代碼質量
編寫單測是自測的一部分,編寫新代碼時增加相應的單測,可以幫助我們發現大部分的bug,有助于減少聯調時的調整,提高聯調效率。
- 花更少的時間進行功能測試
功能測試成本相對較高,因為經常需要執行一系列操作以驗證結果是否符合預期。如果問題如果發現了問題,溝通和復測往往要花費很多的時間。
- 花更少的時間進行回歸測試
回歸測試是為了避免在對應用程序進行更改時引入bug。測試人員不僅要測試他們的新特性,還要測試以前存在的特性,以驗證之前實現的特性是否仍然像預期的那樣運行。 通過單元測試,可以在每次構建之后,重新運行整個測試流程,以確保新代碼不會破壞已有功能
- 測試異常場景
一些異常的場景QA不好構造,比如并發出款是否資金安全,事務異常相關測試等等。而問題經常出現在這些異常的場景,可能引發線上問題甚至是事故。 而單元測試可通過mock的方式方便的模擬各種異常場景。
Go 單元測試工具
gomonkey
引入 gomonkey 有如下好處:
- 隔離被測代碼
- 加速執行測試
- 使執行變得確定
- 模擬特殊情況
功能列表
- 支持為一個函數打一個樁
- 支持為一個函數打一個特定的樁序列
- 支持為一個成員方法打一個樁
- 支持為一個成員方法打一個特定的樁序列
- 支持為一個函數變量打一個樁
- 支持為一個函數變量打一個特定的樁序列
- 支持為一個接口打樁
- 支持為一個接口打一個特定的樁序列
- 支持為一個全局變量打一個樁
函數打樁, 對變量的 mock 實現原理跟 gostub 一樣都是通過 reflect 包實現的。除了 mock 變量,gomonkey 還可以直接 mock 導出函數/方法、mock 代碼所在包的非導出函數
Go monkey Permission Denied 解決方案:https://github.com/eisenxp/macos-golink-wrapper
mv $GOROOT/pkg/tool/darwin_amd64/link $GOROOT/pkg/tool/darwin_amd64/original_link cp https://github.com/eisenxp/macos-golink-wrapper/link $GOROOT/pkg/tool/darwin_amd64/link
下載文件,然后再 cp
wget https://raw.githubusercontent.com/eisenxp/macos-golink-wrapper/main/link
gomonkey 提供了如下 mock 方法:
- ApplyGlobalVar(target, double interface{}):使用 reflect 包,將 target 的值修改為 double
- ApplyFuncVar(target, double interface{}):檢查 target 是否為指針類型,與 double 函數聲明是否相同,最后調用 ApplyGlobalVar
- ApplyFunc(target, double interface{}):修改 target 的機器指令,跳轉到 double 執行
- ApplyMethod(target reflect.Type, methodName string, double interface{}):修改 method 的機器指令,跳轉到 double 執行
- ApplyFuncSeq(target interface{}, outputs []OutputCell):修改 target 的機器指令,跳轉到 gomonkey 生成的一個函數執行,每次調用會順序從 outputs 取出一個值返回
- ApplyMethodSeq(target reflect.Type, methodName string, outputs []OutputCell):修改 target 的機器指令,跳轉到 gomonkey 生成的一個方法執行,每次調用會順序從 outputs 取出一個值返回
- ApplyFuncVarSeq(target interface{}, outputs []OutputCell):gomonkey 生成一個函數順序返回 outputs 中的值,調用 ApplyGlobalVar
gomonkey 打樁失敗的可能原因
- gomonkey 不是并發安全的。如果有多協程并發對同一個目標的打樁的情況,則需要將之前的協程先優雅退出。
- 打樁目標為內聯的函數或成員方法。可通過命令行參數 -gcflags=-l (go1.10 版本之前)或-gcflags=all=-l(go1.10 版本及之后)關閉內聯優化。
- gomonkey 對于私有成員方法的打樁失敗。go1.6 版本的反射機制支持私有成員方法的查詢,而 go1.7 及之后的版本卻不支持,所以當用戶使用 go1.7 及之后的版本時,gomonkey 對于私有成員方法的打樁會觸發異常。
goconvey
為全局變量打一個樁
package unittest import ( "testing" "github.com/agiledragon/gomonkey" "github.com/smartystreets/goconvey/convey" ) var num = 10 //全局變量 func TestApplyGlobalVar(t *testing.T) { convey.Convey("TestApplyGlobalVar", t, func() { convey.Convey("change", func() { patches := gomonkey.ApplyGlobalVar(&num, 150) defer patches.Reset() convey.So(num, convey.ShouldEqual, 150) }) convey.Convey("recover", func() { convey.So(num, convey.ShouldEqual, 10) }) }) }
執行結果:
=== RUN ? TestApplyGlobalVar
..
2 total assertions--- PASS: TestApplyGlobalVar (0.00s)
PASS
為一個函數打樁
func networkCompute(a, b int) (int, error) { // do something in remote computer c := a + b return c, nil } func Compute(a, b int) (int, error) { sum, err := networkCompute(a, b) return sum, err } func TestFunc(t *testing.T) { // mock 了 networkCompute(),返回了計算結果2 patches := gomonkey.ApplyFunc(networkCompute, func(a, b int) (int, error) { return 2, nil }) defer patches.Reset() sum, err := Compute(1, 2) println("expected %v, got %v", 2, sum) if sum != 2 || err != nil { t.Errorf("expected %v, got %v", 2, sum) } }
結果:
=== RUN ? TestFunc
expected %v, got %v 2 3
? ? mock_func_test.go:91: expected 2, got 3
--- FAIL: TestFunc (0.00s)FAIL
可以看到上面的結果,執行時失敗的,mock
沒有成功。
有時會遇到mock失效的情況,這個問題一般是內聯導致的。
什么是內聯?
為了減少函數調用時的堆棧等開銷,對于簡短的函數,會在編譯時,直接內嵌調用的代碼。
我們禁用下內聯,然后執行, go test -v -gcflags=-l mock_func_test.go
執行結果:
=== RUN ? TestFunc
expected %v, got %v 2 2
--- PASS: TestFunc (0.00s)
PASS
對于 go 1.10以下版本,可使用-gcflags=-l禁用內聯,對于go 1.10及以上版本,可以使用-gcflags=all=-l。但目前使用下來,都可以。 關于gcflags
的用法,可以使用 go tool compile --help
查看 gcflags
各參數含義
原文鏈接:https://juejin.cn/post/7111691109528502286
相關推薦
- 2022-05-10 torch.cuda.is_available()返回false最終解決方案
- 2022-06-16 C語言詳解鏈式隊列與循環隊列的實現_C 語言
- 2022-06-01 STL中vector的使用你了解嗎_C 語言
- 2022-02-10 el-date-picker只能選擇當前時間及之前的時間
- 2022-07-29 Linux進程管理方法介紹_linux shell
- 2022-11-30 Rust實現面向對象的方法_Rust語言
- 2022-04-11 關于出現“Content type ‘application/x-www-form-urlencod
- 2022-05-02 利用numba讓python速度提升百倍_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支