天天看點

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

先看看效果吧

當不小心點開網頁文檔的時候,是不是有看到過這樣的展示示例代碼的tab?

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

或者在IDE裡看很多函數的簡介時是不是有這樣的:

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

有沒心生敬畏,覺得很神奇?

其實這就是Golang的testing提供的一種能力,把單測代碼同時作為文檔,我們來看看要怎麼實作吧。

可測試的Example

Example測試會被展示進package的文檔,并可以如單測一樣被驗證。可以在godoc展示的web網頁中特别展示出來。

Example本身也是測試,它會和正常的測試一起編譯和執行,和它們一樣放在xxx_test.go的檔案中,但是它的函數簽名不一樣。

函數簽名

Example的函數簽名以Example開頭,後面緊跟被示例的函數的名字,并且沒有入參:

func ExampleFuncName() {
	……
}
           

比如我現在實作一個函數 AdjacentMixString:

util.go

……
// 互動地混合兩個字元串
func AdjacentMixString(str1, str2 string) string {
	builder := strings.Builder{}
	builder.Grow(len(str1) + len(str2))
	i := 0
	for ; i < len(str1) && i < len(str2); i++ {
		builder.WriteByte(str1[i])
		builder.WriteByte(str2[i])
	}
	if len(str1) > i {
		builder.WriteString(str1[i:])
	}
	if len(str2) > i {
		builder.WriteString(str2[i:])
	}
	return builder.String()
}
           

那它的示例就應該這麼寫:

func ExampleAdjacentMixString() {
	……
}
           

執行測試

和普通的測試一樣,你可以通過go test指令以及相關的option來運作Example測試。

我們來随便寫個Example到util_test.go檔案中:

……
func ExampleAdjacentMixString() {
	// should output 1a2b3c
	AdjacentMixString("123", "abc")
}
           

已經可以看到效果了:

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

然後來運作它:

admin@……/util$ go test -v -run=AdjacentMixString
=== RUN   ExampleAdjacentMixString
--- PASS: ExampleAdjacentMixString (0.00s)
PASS
ok      ……/util    1.229s
           

執行通過了,但好像啥也沒測。

目前為止這個Example還不具備“測試”的作用,僅僅是一個能編譯通過的“文檔”。

Output注釋

Example測試不提供Fail等方法,但是可以通過标準輸出來實作簡單的斷言。

在執行Example測試時,測試架構會截獲标準輸出,并與"output:"後的注釋進行比較。如果相等的話,就能通過測試。

這樣,我們改造我們的Example。

func ExampleAdjacentMixString() {
	fmt.Println(AdjacentMixString("", "abc"))
	fmt.Println(AdjacentMixString("123", ""))
	fmt.Println(AdjacentMixString("1", "abc"))
	fmt.Println(AdjacentMixString("123", "a"))
	fmt.Println(AdjacentMixString("123", "abcd"))
	fmt.Println(AdjacentMixString("1234", "abc"))
	// output:
	// abc
	// 123
	// 1abc
	// 1a23
	// 1a2b3cd
	// 1a2b3c4
}
           

再次運作。又通過了。和上面的輸出幾乎一緻就不貼了。

我們故意改錯一點,把output後的abc改成a看看是什麼樣的:

admin@……/util ‹release*›$ go test -v -run=AdjacentMixString
=== RUN   ExampleAdjacentMixString
--- FAIL: ExampleAdjacentMixString (0.00s)
got:
abc
123
1abc
1a23
1a2b3cd
1a2b3c4
want:
a
123
1abc
1a23
1a2b3cd
1a2b3c4
FAIL
exit status 1
FAIL    ……/util    1.316s
           

可以看到,這個Example成功發揮了一個“測試”的功能。

而文檔也變成了這個樣子:

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語
Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

Example的命名約定

Godoc使用特定的命名約定來關聯Example與其對應的被示例對象。

func ExampleFoo() // 關聯到Foo函數或類型
func ExampleA_Go() // 關聯到A類型的Go方法
func Example() // 關聯整個package

           

還可以通過下劃線加小寫字母的字尾,給同一個對象指定多個Example。比如可以這麼搞:

func ExampleAdjacentMixString() {
	fmt.Println(AdjacentMixString("123", "abcd"))
	// output:
	// 1a2b3cd
}

func ExampleAdjacentMixString_thisIsB() {
	fmt.Println(AdjacentMixString("123", ""))
	// output:
	// 123
}

func ExampleAdjacentMixString_thisIsC() {
	fmt.Println(AdjacentMixString("", "abc"))
	// output:
	// abc
}
           

這樣文檔中的效果就是:

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語
Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

整個檔案作為Example

有的時候一個Example函數不足以表達整個子產品的功能,這個時候可以使用整個檔案作為Example。比如我給util package做一個Example檔案:

util_test.go

package util

import "fmt"

type A struct {
	hi string
}

func (a *A) Go() {
	fmt.Println("This is a file Example")
}

func Example() {
	(&A{}).Go()

	// Output:
	// This is a file Example
}
           

這樣,我就可以在godoc上看到整個檔案了:

Golang如何寫能進入文檔的測試先看看效果吧可測試的Example結語

同樣的,你也可以建多個檔案,每個檔案中的Example函數用不同的下劃線字尾,來為同一個package建多個示例。

unordered output

最後,如果你的輸出是無法确定順序的,可以使用unordered output來代替output:

func ExampleUnorderedOuput() {
	m := map[string]string{
		"a": "b",
		"c": "d",
		"e": "f",
	}
	for k, v := range m {
		fmt.Println(k, v)
	}
	// unordered output:
	// a b
	// c d
	// e f
}
           

結語

今天我們學習了golang的一個很有意思的特性,Example測試。又漲了點姿勢對吧哈哈哈。