Skip to main content

Command Palette

Search for a command to run...

Go 的測試到底要放哪?

test package

Updated
3 min read

在 Line 群上有至少多位網友問過類似下面的問題.

  1. 那各位在比較正式的專案還會用 service locator / ioc container 先把依賴或依賴的工廠集中起來嗎? (所以測試的時候 要特別去引用 service locator 把依賴從 測試取出?)

  2. xxx_test.go 的 package name 會加上 test 嗎? (目前觀察到的差異是加上 test 會 access 不到小寫開頭的變數)

常見的有

  1. 跟待測試模組同資料夾, 檔名是_test.go, 且跟待測試模組同package, 稱為內部測試文件.

  2. 跟待測試模組同資料夾, 檔名是_test.go, 且跟待測試模組不同package, 他的package 名稱會是 `xxx_test, 稱為 外部測試文件.

  3. 通常在根目錄有個獨立的 test 資料夾.

go list

當使用 Go 語言進行開發時,測試是一個不可或缺的部分。go list 命令是一個強大的工具,可以用來檢視和操作 Go 模組的資訊。

go list 通常會搭配 -f 一起使用, -f 指定輸出格式,通常使用 Go 模板語法。.

Term回傳類型描述
ImportPathstring當前 package 下套件的導入路徑
TestGoFiles[]string列舉當前package中的內部測試文件(同package名稱,_test.go 結尾)。
XTestGoFiles[]string當前package中的外部測試文件(不同packge名,_test.go 結尾)
TestImports[]string內部測試文件中導入的所有package的導入路徑。
XTestImports[]string外部測試文件中導入的所有package的導入路徑。

有以上這些我就能在有 go.mod 的專案中來快速查找有沒有測試文件, 分別又是內部或是外部測試文件.

像是我在 opentelemetry-go-contrib 專案中分別輸入以下兩個指令, 會出現不同的查找結果.

go list ./… 這個命令將列出當前目錄及所有子目錄中的所有包的導入路徑。

go list -f '{{.ImportPath}}: {{.XTestGoFiles}}' ./... 列出所有外部測試文件 (XTestGoFiles)。

go list -f '{{.ImportPath}}: {{.TestGoFiles}}' ./... 列出所有內部測試文件 (TestGoFiles)。

> opentelemetry-go-contrib/detectors/aws/ecs
go list ./...
# go.opentelemetry.io/contrib/detectors/aws/ecs
# go.opentelemetry.io/contrib/detectors/aws/ecs/test

go list -f '{{.ImportPath}}: {{.XTestGoFiles}}' ./...
# go.opentelemetry.io/contrib/detectors/aws/ecs: [version_test.go]
# go.opentelemetry.io/contrib/detectors/aws/ecs/test: []

go list -f '{{.ImportPath}}: {{.TestGoFiles}}' ./...
# go.opentelemetry.io/contrib/detectors/aws/ecs: [ecs_test.go]
# go.opentelemetry.io/contrib/detectors/aws/ecs/test: [ecs_test.go]

當然也能自由組合, 例如 go list -f '{{.ImportPath}}: TestGoFiles: {{.TestGoFiles}} XTestGoFiles: {{.XTestGoFiles}} Imports: {{.Imports}} Deps: {{.Deps}}' ./..., 列出每個 package 的內部測試文件、外部測試文件和所有依賴。 能自己玩看看。


go testing 套件的一開始說明有一段, 這裡就在講述內部測試文件外部測試文件.

To write a new test suite, create a file that contains the TestXxx functions as described here, and give that file a name ending in "_test.go". The file will be excluded from regular package builds but will be included when the "go test" command is run.

The test file can be in the same package as the one being tested, or in a corresponding package with the suffix "_test".

If the test file is in the same package, it may refer to unexported identifiers within the package.

If the file is in a separate "_test" package, the package being tested must be imported explicitly and only its exported identifiers may be used. This is known as "black box" testing.

內部測試文件

跟待測試模組同資料夾, 檔名是_test.go, 且跟待測試模組同package。也就是說.

tree
.
├── go.mod
├── go.sum
├── mux.go
├── mux_test.go


cat mux_test.go
> package mux

雖然兩個是同一個package, 但此時你能做實驗, 在 exporter_test.go裡面宣告的public 成員其實在 非 _test.go 的一般模組中是無法被引用的. 這點在 go 官方網站講到 build 時也有提到.

When compiling packages, build ignores files that end in '_test.go'.

外部測試文件

待補

獨立 test 資料夾

我一樣以 opentelemetry-go-contrib 專案來做說明.

cd opentelemetry-go-contrib/instrumentation/github.com/gorilla/mux/otelmux
tree
.
├── go.mod
├── go.sum
├── mux.go
├── mux_test.go
├── test
│   ├── doc.go
│   ├── go.mod
│   ├── go.sum
│   ├── mux_test.go
│   ├── version.go
│   └── version_test.go
├── version.go
└── version_test.go

在 Go 專案中,測試通常與被測試的程式碼放在同一個模組中,使用相同或不同的 package 名稱來進行內部測試或外部測試。然而,有時候為了特定需求,我們可能會將測試代碼放在一個獨立的模組中。這意味著測試程式碼有自己獨立的 go.mod 文件,並且可以擁有不同的依賴,這與主模組的依賴隔離開來。

為什麼使用獨立測試模組?

  1. 隔離依賴

    • 避免傳遞性依賴:主模組(例如 otelmux 的主要模組)不需要依賴預設的 SDK,這樣使用 otelmux 的用戶不會因為主模組的依賴而被迫引入預設 SDK。

    • 獨立管理測試依賴:測試模組可以單獨管理其依賴,如預設 SDK,這些依賴僅在測試時使用,不會影響主模組的依賴樹。

  2. 支持更複雜的測試場景

    • 整合測試:獨立模組可以更容易地進行整合測試,因為它們可以引入更多的依賴和工具來模擬真實的使用場景。

    • 專門的測試工具:可以引入專門用於測試的工具套件(如 testutil),而不會干擾主模組的依賴。

  3. 提升模組的可維護性和靈活性

    • 減少主模組的依賴:保持主模組的依賴輕量,提升其可維護性和靈活性。

    • 獨立更新測試程式碼:測試模組可以獨立更新和升級,而不會影響主模組的穩定性。

  4. 避免循環導入(Import Cycle)

    • 如您所提到的,循環導入會導致 Go 編譯失敗。獨立測試模組可以避免這種情況,因為測試模組不需要反向引用主模組。

    • 測試模組可以作為獨立的實體存在,避免因測試需求而導致的循環依賴問題。

  5. [缺] 增加項目結構的複雜性

    • 需要管理多個 go.mod 文件和模組,增加了項目的結構複雜性。

    • 對於新手來說,可能需要花更多時間來理解和管理多模組項目。

  6. [缺] 可能的重複程式碼

    • 在某些情況下,主模組和測試模組可能需要共享一些程式碼,這可能導致程式碼重複或需要額外的程式碼共享機制。
  7. [缺] 需要額外的配置和維護

    • 需要為獨立測試模組設定與配置 CI/CD pipeline、測試覆蓋率報告等,增加了維護的工作量。

參考資料

Golang - test package 在內跟在外的差別

276 views

More from this blog

Claude Code 監控秘錄:OpenTelemetry(OTel/OTLP)實戰指南

稟告主公:此乃司馬懿進呈之兵書,詳解如何以 OpenTelemetry 陣法,令臥龍神算之一舉一動盡在掌握,知糧草消耗、察兵器效能、辨戰報異常,使主公運籌帷幄於大帳之中。 為何需要斥候情報? 司馬懿稟告主公: 臥龍神算(Claude Code)乃當世利器,然若無斥候回報,主公便如蒙眼行軍——兵器耗損幾何、糧草消費幾許、哪路斥候出了差錯,一概不知。臣以為,此乃兵家大忌。 無情報之弊,有四: 軍

Feb 19, 202610 min read162
Claude Code 監控秘錄:OpenTelemetry(OTel/OTLP)實戰指南

工程師的 Claude Code 實戰指南:從零開始到高效開發

工程師的 Claude Code 實戰指南:從零開始到高效開發 本文整合 Anthropic 官方 Best Practices 與社群實戰 Tips,帶你由淺入深掌握 Claude Code。 什麼是 Claude Code?為什麼值得學? 如果你還在用「複製程式碼貼到 ChatGPT,再複製答案貼回去」的工作流程,Claude Code 會讓你大開眼界。 Claude Code 是 Anthropic 推出的命令列工具,它直接活在你的 terminal 裡,能夠讀懂你的整個 codeb...

Feb 18, 20265 min read72
工程師的 Claude Code 實戰指南:從零開始到高效開發

System Design Interview Ch 12 Digital Wallet

確立問題與設計範疇 角色對話內容 面試者我們應該只關注兩個數位錢包之間的餘額轉帳操作嗎?我們是否需要擔心其他功能? 面試官讓我們只關注餘額轉帳操作。 面試者該系統需要支援多少 TPS(每秒交易次數)? 面試官讓我們假設是 1,000,000 TPS (每秒 100 萬次交易)。 面試者數位錢包對正確性有嚴格的要求。我們可以假設事務保證 就足夠了嗎? 面試官聽起來不錯。 面試者我們需要證明正確性嗎? 面試官這是一個很好的問題。正確性(Correctness)通常只有在交...

Feb 2, 202610 min read190
System Design Interview Ch 12 Digital Wallet

Claude Code 利用 Event-Driven Hooks 打造自動化開發大腦

在現代 AI 輔助開發中,我們不僅需要 AI 寫程式,更需要它懂規則、記性好,並且能自動處理那些繁瑣的雜事。透過 Claude Code Hooks 機制,我們可以介入 AI 的思考與執行迴圈,實現真正的「人機協作自動化」。 一、 動機與痛點:為什麼你需要介入 AI 的生命週期? 在預設狀態下,Claude Code 雖然強大,但它是「被動」且「無狀態」的,這導致了開發者常遇到以下痛點: 記憶重置 (Session Amnesia): 痛點:每次重啟終端機,AI 就像失憶一樣。 解法:你...

Jan 24, 20266 min read441
Claude Code 利用 Event-Driven Hooks 打造自動化開發大腦
M

MicroFIRE

71 posts