3.8 KiB
11.2.2. 測試一個命令
對於測試包go test
是一個的有用的工具,但是稍加努力我們也可以用它來測試可執行程序。如果一個包的名字是 main,那麽在構建時會生成一個可執行程序,不過main包可以作爲一個包被測試器代碼導入。
讓我們爲2.3.2節的echo程序編寫一個測試。我們先將程序拆分爲兩個函數:echo函數完成眞正的工作,main函數用於處理命令行輸入參數和echo可能返迴的錯誤。
gopl.io/ch11/echo
// Echo prints its command-line arguments.
package main
import (
"flag"
"fmt"
"io"
"os"
"strings"
)
var (
n = flag.Bool("n", false, "omit trailing newline")
s = flag.String("s", " ", "separator")
)
var out io.Writer = os.Stdout // modified during testing
func main() {
flag.Parse()
if err := echo(!*n, *s, flag.Args()); err != nil {
fmt.Fprintf(os.Stderr, "echo: %v\n", err)
os.Exit(1)
}
}
func echo(newline bool, sep string, args []string) error {
fmt.Fprint(out, strings.Join(args, sep))
if newline {
fmt.Fprintln(out)
}
return nil
}
在測試中我們可以用各種參數和標標誌調用echo函數,然後檢測它的輸出是否正確, 我們通過增加參數來減少echo函數對全局變量的依賴。我們還增加了一個全局名爲out的變量來替代直接使用os.Stdout,這樣測試代碼可以根據需要將out脩改爲不同的對象以便於檢査。下面就是echo_test.go文件中的測試代碼:
package main
import (
"bytes"
"fmt"
"testing"
)
func TestEcho(t *testing.T) {
var tests = []struct {
newline bool
sep string
args []string
want string
}{
{true, "", []string{}, "\n"},
{false, "", []string{}, ""},
{true, "\t", []string{"one", "two", "three"}, "one\ttwo\tthree\n"},
{true, ",", []string{"a", "b", "c"}, "a,b,c\n"},
{false, ":", []string{"1", "2", "3"}, "1:2:3"},
}
for _, test := range tests {
descr := fmt.Sprintf("echo(%v, %q, %q)",
test.newline, test.sep, test.args)
out = new(bytes.Buffer) // captured output
if err := echo(test.newline, test.sep, test.args); err != nil {
t.Errorf("%s failed: %v", descr, err)
continue
}
got := out.(*bytes.Buffer).String()
if got != test.want {
t.Errorf("%s = %q, want %q", descr, got, test.want)
}
}
}
要註意的是測試代碼和産品代碼在同一個包。雖然是main包,也有對應的main入口函數,但是在測試的時候main包隻是TestEcho測試函數導入的一個普通包,里面main函數併沒有被導出,而是被忽略的。
通過將測試放到表格中,我們很容易添加新的測試用例。讓我通過增加下面的測試用例來看看失敗的情況是怎麽樣的:
{true, ",", []string{"a", "b", "c"}, "a b c\n"}, // NOTE: wrong expectation!
go test
輸出如下:
$ go test gopl.io/ch11/echo
--- FAIL: TestEcho (0.00s)
echo_test.go:31: echo(true, ",", ["a" "b" "c"]) = "a,b,c", want "a b c\n"
FAIL
FAIL gopl.io/ch11/echo 0.006s
錯誤信息描述了嚐試的操作(使用Go類似語法),實際的結果和期望的結果。通過這樣的錯誤信息,你可以在檢視代碼之前就很容易定位錯誤的原因。
要註意的是在測試代碼中併沒有調用log.Fatal或os.Exit,因爲調用這類函數會導致程序提前退出;調用這些函數的特權應該放在main函數中。如果眞的有意外的事情導致函數發生panic異常,測試驅動應該嚐試用recover捕獲異常,然後將當前測試當作失敗處理。如果是可預期的錯誤,例如非法的用戶輸入、找不到文件或配置文件不當等應該通過返迴一個非空的error的方式處理。幸運的是(上面的意外隻是一個插麴),我們的echo示例是比較簡單的也沒有需要返迴非空error的情況。