如何处理用于CLI测试的"fmt" golang库包
how to deal with the "fmt" golang library package for CLI testing
免责声明:祝您圣诞快乐,希望我的问题不会打扰您!
sample.go:
package main
import(
"fmt"
"os"
)
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Println("Error: something is wrong!")
os.Exit(1)
} else {
fmt.Println("May the force be with you!")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: something is wrong!
// exit status 1
我对如何在golang测试中使用接口做了很多研究。但到目前为止,我无法完全理解这一点。至少当我需要 "mock"(抱歉使用这个词)golang std 时,我看不到接口如何帮助我。像 "fmt".
这样的库包
我想到了两个场景:
- 使用os/exec测试命令行界面
- wrap fmt 包所以我可以控制并能够检查输出字符串
我不喜欢这两种情况:
- 我体验过实际的命令行是一个复杂且性能不佳的过程(见下文)。也可能有可移植性问题。
- 我相信这是要走的路,但我担心包装 fmt 包可能需要大量工作(至少包装 time 包以进行测试是一项非常重要的任务 (https://github.com/finklabs/ttime) ).
这里的实际问题:还有其他 (better/simpler/idiomatic) 方式吗?
注:我想在纯golang中做这个,我对下一个测试框架不感兴趣。
cli_test.go:
package main
import(
"os/exec"
"testing"
)
func TestCli(t *testing.T) {
out, err := exec.Command("go run sample.go").Output()
if err != nil {
t.Fatal(err)
}
if string(out) != "May the force be with you!\nError: this is broken and not useful!\nexit status 1" {
t.Fatal("There is something wrong with the CLI")
}
}
Kerningham's Book 的第 11 章很好地解决了这个问题。
诀窍是将对 fmt.Printline() 的调用更改为对
fmt.Fprint(out, ...) 其中 out 被初始化为 os.Stdout
这可以在测试工具中被覆盖为新的(bytes.Buffer)允许
测试以捕获输出。
参见 https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo.go 和
https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo_test.go
由 OP 编辑...
sample.go:
package main
import(
"fmt"
"os"
"io"
)
var out io.Writer = os.Stdout // modified during testing
var exit func(code int) = os.Exit
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Fprint(out, "Error: something is wrong!\n")
exit(1)
} else {
fmt.Fprint(out, "May the force be with you!\n")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: this is broken and not useful!
// exit status 1
cli_test.go:
package main
import(
"bytes"
"testing"
)
func TestUsefulPositive(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
s := sample{42}
s.useful()
if out.(*bytes.Buffer).String() != "May the force be with you!\n" {
t.Fatal("There is something wrong with the CLI")
}
}
func TestUsefulNegative(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
code := 0
osexit := exit
exit = func(c int) { code = c }
defer func() { exit = osexit }()
s := sample{0}
s.useful()
if out.(*bytes.Buffer).String() != "Error: something is wrong!\n" {
t.Fatal("There is something wrong with the CLI")
}
if code != 1 {
t.Fatal("Wrong exit code!")
}
}
我是不是漏掉了什么,还是你在说 testable examples?
基本上,它是这样工作的:在 *_test.go
文件中,您需要遵守约定 Example[[T][_M]]
,其中 T
是类型的占位符,M
您想要在 Godoc 中将可测试示例显示为示例代码的方法的占位符。如果只调用函数Example()
,代码将显示为封装示例。
在你例子代码的最后一行下面,你可以这样写注释
// Output:
// Foo
现在 go test
将确保可测试示例函数 完全 输出以下所有内容 // Output:
(包括空格)否则会使测试失败。
这是一个可测试示例的实际示例
func ExampleMongoStore_Get() {
sessionId := "ExampleGetSession"
data, err := ms.Get(sessionId)
if err == sessionmw.ErrSessionNotFound {
fmt.Printf("Session '%s' not found\n", sessionId)
data = make(map[string]interface{})
data["foo"] = "bar"
ms.Save(sessionId, data)
}
loaded, _ := ms.Get(sessionId)
fmt.Printf("Loaded value '%s' for key '%s' in session '%s'",
loaded["foo"],
"foo", sessionId)
// Output:
// Session 'ExampleGetSession' not found
// Loaded value 'bar' for key 'foo' in session 'ExampleGetSession'
}
免责声明:祝您圣诞快乐,希望我的问题不会打扰您!
sample.go:
package main
import(
"fmt"
"os"
)
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Println("Error: something is wrong!")
os.Exit(1)
} else {
fmt.Println("May the force be with you!")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: something is wrong!
// exit status 1
我对如何在golang测试中使用接口做了很多研究。但到目前为止,我无法完全理解这一点。至少当我需要 "mock"(抱歉使用这个词)golang std 时,我看不到接口如何帮助我。像 "fmt".
这样的库包我想到了两个场景:
- 使用os/exec测试命令行界面
- wrap fmt 包所以我可以控制并能够检查输出字符串
我不喜欢这两种情况:
- 我体验过实际的命令行是一个复杂且性能不佳的过程(见下文)。也可能有可移植性问题。
- 我相信这是要走的路,但我担心包装 fmt 包可能需要大量工作(至少包装 time 包以进行测试是一项非常重要的任务 (https://github.com/finklabs/ttime) ).
这里的实际问题:还有其他 (better/simpler/idiomatic) 方式吗? 注:我想在纯golang中做这个,我对下一个测试框架不感兴趣。
cli_test.go:
package main
import(
"os/exec"
"testing"
)
func TestCli(t *testing.T) {
out, err := exec.Command("go run sample.go").Output()
if err != nil {
t.Fatal(err)
}
if string(out) != "May the force be with you!\nError: this is broken and not useful!\nexit status 1" {
t.Fatal("There is something wrong with the CLI")
}
}
Kerningham's Book 的第 11 章很好地解决了这个问题。 诀窍是将对 fmt.Printline() 的调用更改为对 fmt.Fprint(out, ...) 其中 out 被初始化为 os.Stdout
这可以在测试工具中被覆盖为新的(bytes.Buffer)允许 测试以捕获输出。
参见 https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo.go 和 https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo_test.go
由 OP 编辑... sample.go:
package main
import(
"fmt"
"os"
"io"
)
var out io.Writer = os.Stdout // modified during testing
var exit func(code int) = os.Exit
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Fprint(out, "Error: something is wrong!\n")
exit(1)
} else {
fmt.Fprint(out, "May the force be with you!\n")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: this is broken and not useful!
// exit status 1
cli_test.go:
package main
import(
"bytes"
"testing"
)
func TestUsefulPositive(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
s := sample{42}
s.useful()
if out.(*bytes.Buffer).String() != "May the force be with you!\n" {
t.Fatal("There is something wrong with the CLI")
}
}
func TestUsefulNegative(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
code := 0
osexit := exit
exit = func(c int) { code = c }
defer func() { exit = osexit }()
s := sample{0}
s.useful()
if out.(*bytes.Buffer).String() != "Error: something is wrong!\n" {
t.Fatal("There is something wrong with the CLI")
}
if code != 1 {
t.Fatal("Wrong exit code!")
}
}
我是不是漏掉了什么,还是你在说 testable examples?
基本上,它是这样工作的:在 *_test.go
文件中,您需要遵守约定 Example[[T][_M]]
,其中 T
是类型的占位符,M
您想要在 Godoc 中将可测试示例显示为示例代码的方法的占位符。如果只调用函数Example()
,代码将显示为封装示例。
在你例子代码的最后一行下面,你可以这样写注释
// Output:
// Foo
现在 go test
将确保可测试示例函数 完全 输出以下所有内容 // Output:
(包括空格)否则会使测试失败。
这是一个可测试示例的实际示例
func ExampleMongoStore_Get() {
sessionId := "ExampleGetSession"
data, err := ms.Get(sessionId)
if err == sessionmw.ErrSessionNotFound {
fmt.Printf("Session '%s' not found\n", sessionId)
data = make(map[string]interface{})
data["foo"] = "bar"
ms.Save(sessionId, data)
}
loaded, _ := ms.Get(sessionId)
fmt.Printf("Loaded value '%s' for key '%s' in session '%s'",
loaded["foo"],
"foo", sessionId)
// Output:
// Session 'ExampleGetSession' not found
// Loaded value 'bar' for key 'foo' in session 'ExampleGetSession'
}