猿问

如何处理用于 CLI 测试的“fmt”golang 库包

示例.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测试中使用接口做了很多研究。但到目前为止,我无法完全理解这一点。至少当我需要“模拟”(为使用这个词道歉)golang std 时,我看不到接口如何帮助我。库包,如“fmt”。

我想出了两个场景:

  1. 使用 os/exec测试命令行界面

  2. 包装 fmt包,所以我可以控制并能够检查输出字符串

我不喜欢这两种情况:

  1. 我经历了通过实际命令行的复杂和性能不佳(见下文)。也可能有便携性问题。

  2. 我相信这是要走的路,但我担心包装 fmt 包可能需要很多工作(至少包装时间包进行测试结果是一项非平凡的任务(https://github.com/finklabs/ttime ))。

这里的实际问题:还有另一种(更好/更简单/惯用)的方式吗? 注意:我想用纯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")

    }

}


MM们
浏览 149回答 2
2回答

手掌心

Kerningham 的书的第 11 章很好地解决了这个问题。诀窍是将对 fmt.Printline() 的调用更改为对 fmt.Fprint(out, ...) 的调用,其中 out 被初始化为 os.Stdout这可以在测试工具中覆盖为 new(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 。走由 OP 编辑... sample.go:package mainimport(    "fmt"    "os"    "io")var out io.Writer = os.Stdout // modified during testingvar exit func(code int) = os.Exittype 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 1cli_test.go:package mainimport(    "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!")    }}

白衣染霜花

基本上,它是这样工作的:在*_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'}
随时随地看视频慕课网APP

相关分类

Go
我要回答