mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-01 10:38:59 +00:00
109 lines
3.7 KiB
Markdown
109 lines
3.7 KiB
Markdown
### 11.2.2. 測試一個命令
|
|
|
|
|
|
對於測試包 `go test` 是一個的有用的工具, 但是稍加努力我們也可以用它來測試可執行程序. 如果一個包的名字是 main, 那麽在構建時會生成一個可執行程序, 不過 main 包可以作爲一個包被測試器代碼導入.
|
|
|
|
讓我們爲 2.3.2節 的 echo 程序編寫一個測試. 我們先將程序拆分爲兩個函數: echo 函數完成眞正的工作, main 函數用於處理命令行輸入參數和echo可能返迴的錯誤.
|
|
|
|
```Go
|
|
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 文件中的測試代碼:
|
|
|
|
```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 函數併沒有被導出是被忽略的.
|
|
|
|
通過將測試放到表格中, 我們很容易添加新的測試用例. 讓我通過增加下面的測試用例來看看失敗的情況是怎麽樣的:
|
|
|
|
```Go
|
|
{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的情況.
|
|
|
|
|