【go的測試】單測之gomock包與gomonkey包

目錄

使用gomock包

1.?安裝mockgen

2. 定義接口

3.?生成mock文件

4. 在單測中使用mock的函數

5. gomock 包的使用問題

使用gomonkey包

1. mock 一個包函數

2. mock 一個公有成員函數

3. mock 一個私有成員函數


使用gomock包

1.?安裝mockgen

go get -u github.com/golang/mock/gomock
go get -u github.com/golang/mock/mockgen

2. 定義接口

比如我們想要 mock 一個 infra 包中的名為 GetFromRedis 的函數:

// pkg/infra/infra.go 
package infrafunc GetFromRedis(cli *redis.Client, key string) (string, error) {// 為什么這里要打印一下呢,因為一行會觸發內聯優化,影響單測結果...fmt.Println("cannot reach here if is ut")return cli.Get(context.Background(), key).Result()
}

那需要定義一個 interface 將此GetFromRedis 的函數封裝起來:

type Redis interface {GetFromRedis(cli *redis.Client, key string) (string, error)
}

這個 interface 必須定義,否則 gomock 無法生成 mock 方法。

3.?生成mock文件

在命令行執行:

mockgen -source=pkg/infra/infra.go -destination=pkg/infra/mock/mock_infra.go -package=mock

其中 source 為待 mock 的文件,destination 為 mock 文件生成的位置。

mockgen 工具將為你生成?pkg/infra/mock/mock_infra.go 文件。

4. 在單測中使用mock的函數

mock 就可以這樣寫:

// pkg/infra/infra_test.gofunc Test_gomock(t *testing.T) {// 創建gomock控制器ctrl := gomock.NewController(t)defer ctrl.Finish()var redisCli *redis.Client// 創建模擬對象m := mock.NewMockRedis(ctrl)// 定義預期行為m.EXPECT().GetFromRedis(redisCli, "test_key"). // 模擬入參Return("test value", nil)           // 模擬返回值// 調用被測方法,獲取實際結果resVal, err := m.GetFromRedis(redisCli, "test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

5. gomock 包的使用問題

  • 用起來很麻煩,要定義 interface,還要執行 mockgen 命令
  • 無法 mock 私有成員的函數

使用gomonkey包

當你用了 gomonkey 之后!就再也不會想用 gomock 了!(gomonkey打錢)

1. mock 一個包函數

還是上述例子,比如我們想要 mock 一個 infra 包中的名為 GetFromRedis 的函數:

// pkg/infra/infra.go 
package infrafunc GetFromRedis(cli *redis.Client, key string) (string, error) {fmt.Println("cannot reach here if is ut")return cli.Get(context.Background(), key).Result()
}

無需前置操作,直接在單測中寫 mock 代碼:

// pkg/infra/infra_test.gofunc Test_gomonkey_function(t *testing.T) {// 創建gomonkey對象, 模擬GetFromRedis預期行為patch := gomonkey.ApplyFunc(GetFromRedis, func(_ *redis.Client, _ string) (string, error) {return "test value", nil})defer patch.Reset()// 調用被測方法,獲取實際結果resVal, err := GetFromRedis(&redis.Client{}, "test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

ApplyFunc 的第一個參數是被測函數,第二個參數是預期的行為函數(注意入參出參定義要與被測函數完全一致)

當然你還可以用 patch 去 mock 更多函數:

patch.ApplyFunc(...)

2. mock 一個公有成員函數

比如我們有如下公有成員函數?PublicRedisHandler.GetFromRedis:

// pkg/infra/infra.go 
package infratype PublicRedisHandler struct {cli *redis.Client
}func NewPublicRedisHandler(cli *redis.Client) *PublicRedisHandler {return &PublicRedisHandler{cli: cli,}
}func (r *PublicRedisHandler) GetFromRedis(key string) (string, error) {fmt.Println("cannot reach here if is ut")return r.cli.Get(context.Background(), key).Result()
}

在單測中寫法為:

// pkg/infra/infra_test.gofunc Test_gomonkey_public_member(t *testing.T) {// mock 公共成員方法patch := gomonkey.ApplyMethod(reflect.TypeOf(&PublicRedisHandler{}), "GetFromRedis", func(_ *PublicRedisHandler, key string) (string, error) {return "test value", nil})defer patch.Reset()// 調用被測方法,獲取實際結果r := NewPublicRedisHandler(&redis.Client{})resVal, err := r.GetFromRedis("test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

其中?ApplyMethod 的第一個參數是成員的類型,第二個參數是函數名稱,第三個參數是預期的行為函數(注意入參的第一個參數必須是成員對象)

3. mock 一個私有成員函數

那如果我們想要 mock 的函數是一個私有成員下的函數呢:

// pkg/infra/infra.go 
package infratype privateRedisHandler struct {cli *redis.Client
}func newewPrivateRedisHandler(cli *redis.Client) *privateRedisHandler {return &privateRedisHandler{cli: cli,}
}func (r *privateRedisHandler) GetFromRedis(key string) (string, error) {// 為什么這里要打印一下呢,因為一行會觸發內聯優化,影響單測結果...fmt.Println("cannot reach here if is ut")return r.cli.Get(context.Background(), key).Result()
}

那么寫法為:

// pkg/infra/infra_test.gofunc Test_gomonkey_private_member(t *testing.T) {// mock 私有成員方法patch := gomonkey.ApplyPrivateMethod(reflect.TypeOf(&privateRedisHandler{}), "GetFromRedis", func(_ *privateRedisHandler, key string) (string, error) {return "test value", nil})defer patch.Reset()// 調用被測方法,獲取實際結果r := newPrivateRedisHandler(&redis.Client{})resVal, err := r.GetFromRedis("test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

其中?ApplyPrivateMethod 的第一個參數是成員的類型,第二個參數是函數名稱,第三個參數是預期的行為函數(注意入參的第一個參數必須是成員對象)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/84937.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/84937.shtml
英文地址,請注明出處:http://en.pswp.cn/web/84937.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

html實現登錄與注冊功能案例(不寫死且只使用js)

目錄 案例需求 實現思路 代碼參考 login.html register.html 運行效果 升級思路 案例需求 需要一個登錄界面和注冊頁面實現一個較為完整的登錄注冊功能 1.登錄界面沒有登錄限制需求(降低難度),實現基本的登錄判斷需求,彈窗…

PHP is the best language.

PHP很好寫。 眾所周知Python很好寫,Python 也能開發 Web 應用,但和 PHP 相比,在“直接處理網頁”這件事上,PHP 更加貼近底層和原生。 想快速搭建原型或者 B 端后臺工具,不妨用 PHP Laravel 來搞,真的很香…

Mybatis-Plus 在 getOne() 的時候要加上 .last(“limit 1“)

1.先寫結論: 1.為了確保 SQL 查詢只返回一條記錄(當查詢返回多條時會報錯->多為代碼本身問題)。 2.防止數據庫執行全表掃描 3.參考網址:問題記錄:MyBatis-Plus 中 ServiceImpl 類的 getOne_mybatis_無他&唯手熟爾-2048…

C語言:二分搜索函數

一、二分搜索基本概念 二分搜索(Binary Search)是一種在有序數組中查找特定元素的高效算法,時間復雜度為O(log n)。 基本特點: 僅適用于有序數組(升序或降序) 每次比較將搜索范圍減半 比線性搜索(O(n))…

[前端AI]LangChain.js 和 Next.js LLM構建——協助博客撰寫和總結助手

LangChain.js 和 Next.js LLM 后端應用于協助博客撰寫和總結領域是一個非常實用的方向!這涉及到理解和處理文本內容,并生成新的、有結構的信息。 根據您之前提供的代碼和需求,我們可以在此基礎上進行更具針對性的功能規劃和技術實現。 博客…

用 GitHub Issues 做任務管理和任務 List,簡單好用!

說實話,我平時也是一個人寫代碼,每次開完會整理任務最麻煩: 一堆事項堆在聊天里、文檔里,或者散落在郵件里…… 為了理清這些,我通常會做一份 List,標好優先級,再安排到每日的工作里 雖然這個…

每日算法刷題Day35 6.22:leetcode枚舉技巧枚舉中間2道題,用時1h

枚舉中間 對于三個或者四個變量的問題&#xff0c;枚舉中間的變量往往更好算。 為什么&#xff1f;比如問題有三個下標&#xff0c;需要滿足 0≤i<j<k<n&#xff0c;對比一下&#xff1a; 枚舉 i&#xff0c;后續計算中還需保證 j<k。 枚舉 j&#xff0c;那么 i 和…

【教學類-18-06】20250623蒙德里安黑白七款合并WORD(500張、無學號)

背景需要 客戶買了蒙德里安黑白格子7種尺寸,但是不需要學號方塊,并指定要WORD 設計思路 【教學類-18-05】20241118正方形手工紙(蒙德里安-風格派-紅黃藍黑白)-CSDN博客文章瀏覽閱讀1.3k次,點贊29次,收藏18次。【教學類-18-05】20241118正方形手工紙(蒙德里安-風格派-紅…

langchain--(4)

7 Embedding文本向量化 Embedding文本向量化是一種將非結構化文本轉化為低維、連續數值向量的技術,旨在通過數學方式捕捉文本的語義、語法或特征信息,從而讓機器更高效地處理語言任務。其核心思想源于流形假設(Manifold Hypothesis),即認為高維原始數據(如文本)實際隱含…

DMDRS部署實施手冊(ORACLE=》DM)

DMDRS部署實施手冊&#xff08;ORACLE》DM&#xff09; 1 同步說明2 DMDRS安裝3 數據庫準備3.1 源端準備3.1.1 開啟歸檔日志和附加日志3.1.2 關閉回收站3.1.3 創建同步用戶 3.2 目標準備3.2.1 創建同步用戶 4 DMDRS配置4.1 源端配置4.2 目標配置 5 DMDRS啟動5.1 啟動源端服務5.…

十(1)作業:sqli-labs重點關卡

參考文章&#xff1a;詳細sqli-labs&#xff08;1-65&#xff09;通關講解-CSDN博客 第1關&#xff1a; 輸入 &#xff1a; ?id3 輸入 &#xff1a; ?id2 當輸入的數字不同&#xff0c;頁面的響應也不同&#xff0c;說明&#xff0c;輸入的內容被帶入到數據庫里查詢了 輸…

Python 爬蟲入門 Day 7 - 復盤 + 實戰挑戰日

Python 第二階段 - 爬蟲入門 &#x1f3af; 本周知識回顧 網絡請求與網頁結構基礎 HTML解析入門&#xff08;使用 BeautifulSoup&#xff09; 實現爬蟲多頁抓取與翻頁邏輯 模擬登錄爬蟲與 Session 維持 使用 XPath 進行網頁解析&#xff08;lxml XPath&#xff09; 反爬蟲應對…

WebRTC(七):媒體能力協商

目的 在 WebRTC 中&#xff0c;每個瀏覽器或終端支持的音視頻編解碼器、分辨率、碼率、幀率等可能不同。媒體能力協商的目的就是&#xff1a; 確保雙方能“聽得懂”對方發的媒體流&#xff1b;明確誰發送、誰接收、怎么發送&#xff1b;保障連接的互操作性和兼容性。 P2P的基…

可信啟動方案設計

安全之安全(security)博客目錄導讀 目錄 一、引言 二、關鍵數據(Critical Data) 三、度量槽(Measurement Slot) 四、可信啟動后端 1、事件日志(Event Log) 2、離散型 TPM(Discrete TPM) 3、RSE(運行時安全引擎) 五、平臺接口 平臺接口的職責: 1、函數:b…

?通義萬相2.1深度解析:AI視頻生成引擎FLF2V-14B全流程指南(命令行參數+模型架構+數據流)

&#x1f31f; 從零詳解&#xff1a;如何用AI模型生成視頻&#xff1f;命令行、模型結構、數據流全解析&#xff01; 本文通過一個實際案例&#xff0c;詳細解析使用AI模型生成視頻的整個流程。從命令行參數解讀到模型結構&#xff0c;再到數據在模型間的流動&#xff0c;一步步…

在 TypeScript 前端中使用 Umi-Request 調用 Java 接口的完整指南

下面我將詳細介紹如何在基于 TypeScript 的前端項目中使用 umi-request 調用 IntelliJ IDEA 中開發的 Java 接口&#xff0c;包括完整的實現方案和代碼示例。 整體方案設計 一、Java 后端接口準備 1. 創建 Spring Boot 控制器 // src/main/java/com/example/demo/controller…

GO Gin Web框架面試題及參考答案

目錄 Gin 與 net/http 有哪些主要區別?為什么選擇 Gin? 如何使用 Gin 啟動一個 HTTP 服務并設置默認路由? Gin 的默認路由和自定義路由器組是如何工作的? 如何在 Gin 中綁定請求參數(Query、Form、JSON、XML)? 如何在 Gin 中使用中間件?中間件執行順序是怎樣的? …

asp.net core Razor動態語言編程代替asp.net .aspx更高級嗎?

For Each item In products<tr><td>item.Id</td><td>item.Name</td><td>item.Price.ToString("C")</td></tr>Next為什么要用<tr> ? 在Blazor的Razor語法中&#xff0c;使用<tr>是為了在VB.NET代碼塊中…

css語法中的選擇器與屬性詳解:嵌套聲明、集體聲明、全局聲明、混合選擇器

嵌套聲明 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>嵌套聲明</title> <!-- 這里p span 的含義是p標簽下面的span標簽 所以有嵌套關系--><style>p span {font-weight:…

Linux 系統中,/usr/bin/ 和/bin/的區別?

在 Linux 系統中&#xff0c;/bin/ 和 /usr/bin/ 都是存放可執行程序&#xff08;命令&#xff09;的目錄&#xff0c;但它們在歷史定位、用途、掛載策略和系統設計上有一定區別。 ? 快速對比總結 項目/bin//usr/bin/全稱含義binary&#xff08;核心二進制&#xff09;user b…