需要在Golang中进行交互式输入的测试执行程序

时间:2019-01-31 22:26:08

标签: shell go exec fzf

正在尝试在Go代码中使用fzf。我提到了作者here给出的示例。当我尝试为该功能编写测试时,卡住为fzf时需要交互输入。

代码:

func withFilter(command string, input func(in io.WriteCloser)) []string {
    shell := os.Getenv("SHELL")
    if len(shell) == 0 {
        shell = "sh"
    }
    cmd := exec.Command(shell, "-c", command)
    cmd.Stderr = os.Stderr
    in, _ := cmd.StdinPipe()
    go func() {
        input(in)
        in.Close()
    }()
    result, _ := cmd.Output()
    return strings.Split(string(result), "\n")
}

func filter() []string {
    filtered := withFilter("fzf -m", func(in io.WriteCloser) {
        for i := 0; i < 10; i++ {
            fmt.Fprintln(in, i)
            time.Sleep(5 * time.Millisecond)
        }
    })

    return filtered
}

测试:

func TestFilter(t *testing.T) {
    assert.Equal(t, []string{"1", "2", "3"}, filter())
}

我尝试调试,但发现它停留在cmd.Output()处。通过更深入地挖掘,看起来该命令将无限期地等待输入,但是不确定如何以编程方式提供它。我尝试将\n写入os.Stdin,但是没有用。

任何人的指点或解释将不胜感激。谢谢。

1 个答案:

答案 0 :(得分:1)

go之美:其io.Reader / io.Writer界面。

您必须提供withFilter(扩展名为filter)方法的读取位置。因此,必须提供in

// example

func readFrom(reader io.Reader) {
    r := bufio.NewScanner(reader)

    for r.Scan() {
        fmt.Println("got", r.Text())
    }

    fmt.Println("Done.")
}

func main() {
    const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"

    readFrom(strings.NewReader(input))

    // okay, with stdin
    readFrom(os.Stdin)

}

如您所见,第一个readFrom是完全可测试的,只需将input变量更改为要测试的变量即可。第二个readFrom仅在stdin关闭时返回。

解决方案是不是写入os.Stdin。考虑os.Stdin在您的操作系统中的实现细节,而不是您必须在测试中包括的东西。

https://play.golang.org/p/D2wzHYrV2TM (os.Stdin在操场上关闭)