Go 部落格
Go 中的可測試示例
引言
Godoc 中的 示例 是 Go 程式碼片段,它們會顯示為包文件,並透過執行它們作為測試來驗證。使用者訪問包的 godoc 網頁並點選相應的“執行”按鈕也可以執行它們。
為包提供可執行的文件可以保證資訊不會隨著 API 的更改而過時。
標準庫包含許多此類示例(例如,請參閱 strings
包)。
本文將介紹如何編寫自己的示例函式。
示例也是測試
示例會作為包測試套件的一部分進行編譯(並且可以選擇性地執行)。
與典型測試一樣,示例是位於包的 _test.go
檔案中的函式。但與普通測試函式不同的是,示例函式不接受任何引數,並且以單詞 Example
而不是 Test
開頭。
reverse
包 是 Go 示例倉庫的一部分。下面是一個演示其 String
函式的示例:
package reverse_test
import (
"fmt"
"golang.org/x/example/hello/reverse"
)
func ExampleString() {
fmt.Println(reverse.String("hello"))
// Output: olleh
}
此程式碼可能位於 reverse
目錄下的 example_test.go
檔案中。
Go 包文件伺服器 pkg.go.dev 將此示例與 String
函式的文件一起展示。

執行包的測試套件,我們可以看到示例函式已經執行,無需我們進一步的安排。
$ go test -v
=== RUN TestString
--- PASS: TestString (0.00s)
=== RUN ExampleString
--- PASS: ExampleString (0.00s)
PASS
ok golang.org/x/example/hello/reverse 0.209s
輸出註釋
ExampleString
函式“透過”是什麼意思?
在執行示例時,測試框架會捕獲寫入標準輸出的資料,然後將輸出與示例的“Output:”註釋進行比較。如果測試的輸出與其輸出註釋匹配,則測試透過。
要檢視失敗的示例,我們可以將輸出註釋文字更改為明顯不正確的文字
func ExampleString() {
fmt.Println(reverse.String("hello"))
// Output: golly
}
然後再次執行測試。
$ go test
--- FAIL: ExampleString (0.00s)
got:
olleh
want:
golly
FAIL
如果我們完全移除輸出註釋
func ExampleString() {
fmt.Println(reverse.String("hello"))
}
那麼示例函式將被編譯但不會執行。
$ go test -v
=== RUN TestString
--- PASS: TestString (0.00s)
PASS
ok golang.org/x/example/hello/reverse 0.110s
沒有輸出註釋的示例對於演示無法作為單元測試執行的程式碼(例如訪問網路的程式碼)非常有用,同時又能保證示例至少可以編譯透過。
示例函式名稱
Godoc 使用命名約定將示例函式與包級識別符號關聯起來。
func ExampleFoo() // documents the Foo function or type
func ExampleBar_Qux() // documents the Qux method of type Bar
func Example() // documents the package as a whole
遵循此約定,godoc 會將 ExampleString
示例與 String
函式的文件一起顯示。
可以透過使用以下劃線後跟小寫字母開頭的字尾,為給定的識別符號提供多個示例。這些示例都記錄了 String
函式。
func ExampleString()
func ExampleString_second()
func ExampleString_third()
大型示例
有時,編寫一個好的示例需要的不只是一個函式。
例如,要演示 sort
包,我們應該展示 sort.Interface
的實現。由於方法不能在函式體內宣告,因此示例除了示例函式外,還需要包含一些上下文。
要實現這一點,我們可以使用“整個檔案示例”。整個檔案示例是一個以 _test.go
結尾的檔案,其中包含恰好一個示例函式、沒有測試或基準測試函式,並且至少有一個其他包級宣告。顯示此類示例時,godoc 將顯示整個檔案。
這是 sort
包的一個整個檔案示例:
package sort_test
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s: %d", p.Name, p.Age)
}
// ByAge implements sort.Interface for []Person based on
// the Age field.
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func Example() {
people := []Person{
{"Bob", 31},
{"John", 42},
{"Michael", 17},
{"Jenny", 26},
}
fmt.Println(people)
sort.Sort(ByAge(people))
fmt.Println(people)
// Output:
// [Bob: 31 John: 42 Michael: 17 Jenny: 26]
// [Michael: 17 Jenny: 26 Bob: 31 John: 42]
}
一個包可以包含多個整個檔案示例;每個檔案一個示例。請檢視 sort
包的原始碼以實際瞭解其用法。
結論
Godoc 示例是編寫和維護程式碼作為文件的絕佳方式。它們還提供了可編輯、可用、可執行的示例供使用者構建。請使用它們!
下一篇文章:GopherChina 之行報告
上一篇文章:包名稱
部落格索引