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

Am trying to use fzf in a Go code. I referred to the example given here by the author. When I tried to write a test for the function its getting stuck as fzf requires interactive input.

Code:

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), "
")
}

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
}

Test:

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

I tried debugging and noticed that it gets stuck at cmd.Output(). By digging a bit deeper it looks like the command is waiting indefinitely for input, however am not sure how to provide it programatically. I tried writing to os.Stdin but it didn't work.

Anyone pointers or explanation would be appreciated. Thanks.

The beauty of go: Its io.Reader/io.Writer interfaces.

You have to provide from where your withFilter (and by extension filter) method should read. So, in has to be provided it.

// 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,
Made glorious summer by this sun of York.
"

    readFrom(strings.NewReader(input))

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

}

As you can see, the first readFrom is fully testable, you just change the input variable to whatever you want to test. The second readFrom will only return when the stdin is closed.

The solution is not to write to os.Stdin. Consider os.Stdin an implementation detail in your OS—not something you have to include in your testing.

https://play.golang.org/p/D2wzHYrV2TM (os.Stdin is closed in the playground)