最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解在Go語言單元測試中如何解決文件依賴問題

 更新時間:2023年08月14日 09:25:22   作者:江湖十年  
現(xiàn)如今的?Web?應用程序往往采用?RESTful?API?接口形式對外提供服務,后端接口直接向前端返回?HTML?文件的情況越來越少,所以在程序中操作文件的場景也變少了,在編寫單元測試時,文件就成了被測試代碼的外部依賴,本文就來講解下測試過程中如何解決文件外部依賴問題

現(xiàn)如今的 Web 應用程序往往采用 RESTful API 接口形式對外提供服務,后端接口直接向前端返回 HTML 文件的情況越來越少,所以在程序中操作文件的場景也變少了。不過有些時候還是需要對文件進行操作,比如某個 API 接口需要返回應用程序的 ChangeLog,那么這個接口就可以通過讀取項目的 CHANGELOG.md 文件內(nèi)容,將其發(fā)送給前端。

在編寫單元測試時,文件就成了被測試代碼的外部依賴,本文就來講解下測試過程中如何解決文件外部依賴問題。

獲取 ChangeLog 程序示例

假設我們有一個函數(shù),可以讀取項目的 ChangeLog 信息并返回。

程序代碼實現(xiàn)如下:

package main
import (
	"io"
	"os"
)
var (
	version        = "dev"
	commit         = "none"
	builtGoVersion = "unknown"
	changeLogPath  = "CHANGELOG.md"
)
type ChangeLogSpec struct {
	Version        string
	Commit         string
	BuiltGoVersion string
	ChangeLog      string
}
func GetChangeLog() (ChangeLogSpec, error) {
	data, err := os.ReadFile(changeLogPath)
	if err != nil {
		return ChangeLogSpec{}, err
	}
	return ChangeLogSpec{
		Version:        version,
		Commit:         commit,
		BuiltGoVersion: builtGoVersion,
		ChangeLog:      string(data),
	}, nil
}

GetChangeLog 函數(shù)實現(xiàn)比較簡單,首先從 changeLogPath 文件路徑中讀取 ChangeLog 內(nèi)容,然后結(jié)合程序版本號、COMMIT 信息、Go 版本號一起組裝成 ChangeLogSpec 結(jié)構體,并返回。

使用臨時文件測試

現(xiàn)在,我們要對 GetChangeLog 函數(shù)進行單元測試。

可以發(fā)現(xiàn),GetChangeLog 函數(shù)內(nèi)部依賴了 changeLogPath 文件路徑,然后從中讀取內(nèi)容。所以,在編寫測試時,我們要考慮 changeLogPath 文件如何指定。

我們最先想到的就是指定 changeLogPath 文件的真實路徑。但是,這可能會存在問題,比如本地環(huán)境和 CI 環(huán)境下 changeLogPath 文件路徑不同,那么在編寫測試代碼時,就要考慮根據(jù)不同的測試環(huán)境執(zhí)行不同邏輯。所以,這種方式不應該成為首選方案。

不過,我們可以換種思路,Go 語言提供了 os.CreateTemp 方法,可以創(chuàng)建一個臨時文件。那么,我們就可以考慮在測試函數(shù)開始時創(chuàng)建一個臨時文件來保存 ChangeLog,然后為 changeLogPath 變量賦值為臨時文件路徑,測試代碼執(zhí)行完成后刪除臨時文件,這樣就能夠解決單元測試中依賴外部文件的問題。

按照這個思路,編寫的單元測試代碼如下:

func TestGetChangeLog(t *testing.T) {
	// 創(chuàng)建臨時文件
	// 第一個參數(shù)傳 "",表示在操作系統(tǒng)的臨時目錄下創(chuàng)建該文件
	// 文件文件名會以第二個參數(shù)作為前綴,剩余的部分會自動生成,以確保并發(fā)調(diào)用時生成的文件名不重復
	f, err := os.CreateTemp("", "TEST_CHANGELOG")
	assert.NoError(t, err)
	defer func() {
		_ = f.Close()
		// 盡管操作系統(tǒng)會在某個時間自動清理臨時文件,但主動清理是創(chuàng)建者的責任
		_ = os.RemoveAll(f.Name())
	}()
	changeLogPath = f.Name()
	data := `
# Changelog
All notable changes to this project will be documented in this file.
`
	_, err = f.WriteString(data)
	assert.NoError(t, err)
	expected := ChangeLogSpec{
		Version:        "v0.1.1",
		Commit:         "1",
		BuiltGoVersion: "1.20.1",
		ChangeLog: `
# Changelog
All notable changes to this project will be documented in this file.
`,
	}
	actual, err := GetChangeLog()
	assert.NoError(t, err)
	assert.Equal(t, expected, actual)
}

我們首先通過 os.CreateTemp("", "TEST_CHANGELOG") 創(chuàng)建了一個臨時文件,然后將 data 內(nèi)容寫入臨時文件作為 ChangeLog,再然后將臨時文件名稱 f.Name() 賦值給 changeLogPath,之后就可以調(diào)用 GetChangeLog 函數(shù)進行測試了。

對于程序版本號、COMMIT 信息、Go 版本號這幾個變量,因為都是全局變量,所以也屬于外部依賴。

對于全局變量的依賴,我們可以在 init 函數(shù)中對其進行初始化,這樣就相當于在測試環(huán)境中固定了這幾個變量的值,便于測試。

func init() {
	version = "v0.1.1"
	commit = "1"
	builtGoVersion = "1.20.1"
}

筆記:你也可以在 TestMain 函數(shù)中對其進行初始化。

使用 go test 來執(zhí)行測試函數(shù):

$ go test -v -run="TestGetChangeLog$"     
=== RUN   TestGetChangeLog
--- PASS: TestGetChangeLog (0.00s)
PASS
ok      github.com/jianghushinian/blog-go-example/test/file     0.562s

測試通過。

使用 Go embed 測試

以上我們介紹了使用臨時文件的方式來解決被測試函數(shù)依賴外部文件的問題。

不過我們在測試中提供的 ChangeLog 內(nèi)容不多:

data := `
# Changelog
All notable changes to this project will be documented in this file.
`

為了讓單元測試更加可靠,你也許想測試 ChangeLog 內(nèi)容比較多的情況下,GetChangeLog 函數(shù)能否正常工作。

我們可以編寫一個真實的 CHANGELOG.md 文件,存放于 testdata/CHANGELOG.md 路徑下:

# Kubernetes v0.1.1
## 主要特性和改進
- 添加了一些新的主要特性和改進。
## 重要變更
- 這里列出了對現(xiàn)有功能的重要變更。
## API 變更
- 在 API 中進行的重要變更和更新。
## 已知問題
- 列出了已知的問題和限制。
## Bug 修復
- 修復了以下已知 Bug。
## 改進和優(yōu)化
- 對現(xiàn)有功能進行了改進和優(yōu)化。
## 安全性更新
- 列出了安全性方面的更新和修復。
## 已棄用功能
- 列出了已被棄用的功能。
## 警告和提醒
- 列出了需要注意的警告和提醒事項。
## 社區(qū)貢獻者
- 致謝并列出了為此版本做出貢獻的社區(qū)成員。
更詳細的信息可以查閱 Kubernetes 官方文檔和發(fā)布說明。

此時,我們可以使用 Go 提供的 embed 技術來將文件內(nèi)容嵌入到 Go 變量中。

embed []byte

embed 可以實現(xiàn)在 Go 程序編譯時,直接將文件內(nèi)容嵌入到 Go 變量。embed 目前支持嵌入兩種基礎類型的變量,分別是 []bytestrings。嵌入這兩種類型變量方式相同,本小節(jié)就像大家演示下如何通過將文件嵌入 []byte 變量的方式來編寫 GetChangeLog 函數(shù)的單元測試。

GetChangeLog 函數(shù)編寫的單元測試代碼如下:

package main
import (
	_ "embed"
	"os"
	"testing"
	"github.com/stretchr/testify/assert"
)
//go:embed testdata/CHANGELOG.md
var changelog []byte
func TestGetChangeLog_by_embed(t *testing.T) {
	f, err := os.CreateTemp("", "TEST_CHANGELOG")
	assert.NoError(t, err)
	defer func() {
		_ = f.Close()
		_ = os.RemoveAll(f.Name())
	}()
	changeLogPath = f.Name()
	_, err = f.Write(changelog)
	assert.NoError(t, err)
	expected := ChangeLogSpec{
		Version:        "v0.1.1",
		Commit:         "1",
		BuiltGoVersion: "1.20.1",
		ChangeLog:      string(changelog),
	}
	actual, err := GetChangeLog()
	assert.NoError(t, err)
	assert.Equal(t, expected, actual)
}

單元測試中,我們最需要關注的是這行代碼:

//go:embed testdata/CHANGELOG.md
var changelog []byte

//go:embed 是一個指令注釋,用來標記嵌入指令,注意冒號 : 前后沒有空格,testdata/CHANGELOG.md 指明要嵌入的文件。

在嵌入指令下方,緊挨著我們定義了變量 var changelog []byte 用來接收被嵌入文件的內(nèi)容。

程序編譯后,變量 changelog 的值就是 testdata/CHANGELOG.md 文件中的內(nèi)容了。

注意,文件開頭的 import 中要導入 embed,嵌入指令才可以使用。

之后的單元測試代碼改動就比較小了,僅用 changelog 變量替換了原來代碼中的 data 變量。

使用 go test 來執(zhí)行測試函數(shù):

$ go test -v -run="TestGetChangeLog_by_embed"
=== RUN   TestGetChangeLog_by_embed
--- PASS: TestGetChangeLog_by_embed (0.00s)
PASS
ok      github.com/jianghushinian/blog-go-example/test/file     0.365s

單元測試仍能通過。

embed fs.FS

Go embed 技術不僅能夠嵌入文件到基礎類型變量,還能直接將文件嵌入為一個文件系統(tǒng)。

為了演示這一強大的功能,我們修改下 GetChangeLog 函數(shù)代碼,讓其接收一個 io.Reader 類型的參數(shù),然后從這個參數(shù)中讀取 ChangeLog 內(nèi)容,而不再是通過讀取指定路徑下的 ChangeLog 內(nèi)容。

修改后程序代碼如下:

func GetChangeLogByIOReader(reader io.Reader) (ChangeLogSpec, error) {
	data, err := io.ReadAll(reader)
	if err != nil {
		return ChangeLogSpec{}, err
	}
	return ChangeLogSpec{
		Version:        version,
		Commit:         commit,
		BuiltGoVersion: builtGoVersion,
		ChangeLog:      string(data),
	}, nil
}

如下是為新的 GetChangeLogByIOReader 函數(shù)編寫的單元測試代碼:

//go:embed testdata/CHANGELOG.md
var fs embed.FS
func TestGetChangeLogByIOReader(t *testing.T) {
	f, err := fs.Open("testdata/CHANGELOG.md")
	assert.NoError(t, err)
	data, err := io.ReadAll(f)
	assert.NoError(t, err)
	// 將數(shù)據(jù)的讀取位置重置到開頭
	_, err = f.(io.ReadSeeker).Seek(0, 0)
	assert.NoError(t, err)
	expected := ChangeLogSpec{
		Version:        "v0.1.1",
		Commit:         "1",
		BuiltGoVersion: "1.20.1",
		ChangeLog:      string(data),
	}
	actual, err := GetChangeLogByIOReader(f)
	assert.NoError(t, err)
	assert.Equal(t, expected, actual)
}

我們同樣使用 //go:embed testdata/CHANGELOG.md 來指定嵌入的文件,不過,這次定義的變量 var fs embed.FS 是一個文件系統(tǒng),里面包含了被嵌入的文件。

在測試代碼中,使用 fs.Open("testdata/CHANGELOG.md") 打開文件內(nèi)容,得到 fs.File 類型對象,之后就可以像其他 Go 文件對象一樣操作它。

使用 go test 來執(zhí)行測試函數(shù):

$ go test -v -run="TestGetChangeLogByIOReader"                         
=== RUN   TestGetChangeLogByIOReader
--- PASS: TestGetChangeLogByIOReader (0.00s)
PASS
ok      github.com/jianghushinian/blog-go-example/test/file     0.135s

測試通過。

總結(jié)

本文向大家介紹了在 Go 中編寫單元測試時,如何解決文件外部依賴的問題。

Go 語言提供了 os.CreateTemp 方法,可以創(chuàng)建一個臨時文件,我們可以利用這個方法來解決文件外部依賴。

此外,Go 語言還提供了 embed 技術,能夠在程序編譯時直接將文件內(nèi)容嵌入到 Go 變量中。這項技術雖然不是為單元測試而生的,但我們可以借此來解決文件外部依賴問題。本文為大家演示了如何將文件嵌入到 []byte 和文件系統(tǒng),兩種方案用法差異不大,可以根據(jù)需求和喜好進行選擇。

本文完整代碼示例我放在了 GitHub 上,歡迎點擊查看。

希望此文能對你有所幫助。

以上就是詳解在Go語言單元測試中如何解決文件依賴問題的詳細內(nèi)容,更多關于Go單元測試解決文件依賴的資料請關注腳本之家其它相關文章!

相關文章

  • goquery 入門(安裝使用教程)

    goquery 入門(安裝使用教程)

    這篇文章主要為大家介紹了goquery 入門(安裝使用)教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • Golang?統(tǒng)計字符串中數(shù)字字母數(shù)量的實現(xiàn)方法

    Golang?統(tǒng)計字符串中數(shù)字字母數(shù)量的實現(xiàn)方法

    這篇文章主要介紹了Golang?統(tǒng)計字符串中數(shù)字字母數(shù)量,本文給出了兩種從字符串獲取數(shù)字與字母數(shù)量的方法,分別是ASCII 碼值和正則表達式,需要的朋友可以參考下
    2022-06-06
  • Go語言中defer語句的用法

    Go語言中defer語句的用法

    這篇文章介紹了Go語言中defer語句的用法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • Go語言生成素數(shù)的方法

    Go語言生成素數(shù)的方法

    這篇文章主要介紹了Go語言生成素數(shù)的方法,實例分析了Go語言生成素數(shù)的技巧,需要的朋友可以參考下
    2015-03-03
  • 詳解如何使用Golang擴展Envoy

    詳解如何使用Golang擴展Envoy

    這篇文章主要為大家介紹了詳解如何使用Golang擴展Envoy實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • Go 互斥鎖和讀寫互斥鎖的實現(xiàn)

    Go 互斥鎖和讀寫互斥鎖的實現(xiàn)

    本文主要介紹了Go 互斥鎖和讀寫互斥鎖的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • 詳解Go中指針的原理與引用

    詳解Go中指針的原理與引用

    在?Go?中,指針是強大而重要的功能,它允許開發(fā)人員直接處理內(nèi)存地址并實現(xiàn)高效的數(shù)據(jù)操作,本文主要帶大家了解下指針在?Go?中的工作原理以及對于編寫高效、高性能代碼的重要性,希望對大家有所幫助
    2023-09-09
  • golang?鏈路追蹤的實現(xiàn)示例

    golang?鏈路追蹤的實現(xiàn)示例

    本文主要介紹了golang?鏈路追蹤的實現(xiàn)示例,包括調(diào)用鏈過長和接口響應慢的問題,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • Golang實現(xiàn)協(xié)程超時控制的方式總結(jié)

    Golang實現(xiàn)協(xié)程超時控制的方式總結(jié)

    我們知道,go協(xié)程如果不做好處理,很容易造成內(nèi)存泄漏,所以對goroutine做超時控制,才能有效避免這種情況發(fā)生,本文為大家整理了兩個常見的Golang超時控制方法,需要的可以收藏一下
    2023-05-05
  • 一文掌握Golang的panic和recover實戰(zhàn)

    一文掌握Golang的panic和recover實戰(zhàn)

    Go語言中,異常處理通常依賴error返回值,本文將通過示例展示如何在Go語言中正確使用recover來處理panic異常,防止程序直接崩潰,感興趣的朋友跟隨小編一起看看吧
    2024-09-09

最新評論

滦南县| 磐石市| 庄河市| 兴义市| 卓尼县| 鲜城| 阜南县| 邳州市| 马公市| 凤阳县| 文登市| 武威市| 酒泉市| 昌吉市| 天峻县| 哈巴河县| 泰和县| 若尔盖县| 文安县| 富宁县| 玉树县| 鄱阳县| 黎平县| 罗源县| 垣曲县| 横峰县| 离岛区| 莎车县| 都兰县| 胶州市| 奉节县| 九台市| 皋兰县| 文登市| 金昌市| 大悟县| 健康| 长春市| 呼图壁县| 洱源县| 兰考县|