douhuan3448 2015-12-25 11:13
浏览 29
已采纳

如何处理“ fmt” golang库软件包进行CLI测试

Disclaimer: I wish you a merry XMas and I hope my question does not disturb you!

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

I did a lot of research on how to use interfaces in golang testing. But so far I was not able to wrap my head around this completely. At least I can not see how interfaces help me when I need to "mock" (apologies for using this word) golang std. library packages like "fmt".

I came up with two scenarios:

  1. use os/exec to test the command line interface
  2. wrap fmt package so I have control and am able to check the output strings

I do not like both scenarios:

  1. I experience going through the actual command line a convoluted and not-performant (see below). Might have portability issues, too.
  2. I believe this is the way to go but I fear that wrapping the fmt package might be a lot of work (at least wrapping the time package for testing turned out a non-trivial task (https://github.com/finklabs/ttime)).

Actual Question here: Is there another (better/simpler/idiomatic) way? Note: I want to do this in pure golang, I am not interested in the next testing framework.

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!
Error: this is broken and not useful!
exit status 1" {
        t.Fatal("There is something wrong with the CLI")
    }
}
  • 写回答

2条回答 默认 最新

  • duanjingsen7904 2015-12-25 13:56
    关注

    Chapter 11 of Kerningham's Book gives a good solution to this question. The trick is to change the calls to fmt.Printline() to calls to fmt.Fprint(out, ...) where out is initialised to os.Stdout

    This can be overwritten in the test harness to new(bytes.Buffer) allowing the test to capture the output.

    See https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo.go and https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo_test.go

    edited by 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!
    ")
            exit(1)
        } else {
            fmt.Fprint(out, "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: 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!
    " {
            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!
    " {
            t.Fatal("There is something wrong with the CLI")
        }
        if code != 1 {
            t.Fatal("Wrong exit code!")
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题
  • ¥15 matlab中使用gurobi时报错
  • ¥15 这个主板怎么能扩出一两个sata口
  • ¥15 不是,这到底错哪儿了😭
  • ¥15 2020长安杯与连接网探
  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么