2018-08-06 06:57
浏览 178


I am trying to do functional testing of a cli app similar to this way.

As the command asks a few input on command prompt, I am putting them in a file and setting it as os.Stdin.

cmd := exec.Command(path.Join(dir, binaryName), "myArg")
tmpfile := setStdin("TheMasterPassword
cmd.Stdin = tmpfile
output, err := cmd.CombinedOutput()

The setStdin just creates a tmpFile, write the string in file and returns the *os.File.

Now, I am expecting TheMasterPassword to be first input, and it's working. But for the second input always getting Critical Error: EOF.

The function I am using for asking and getting user input this :

func Ask(question string, minLen int) string {
    reader := bufio.NewReader(os.Stdin)

    for {
        fmt.Printf("%s: ", question)

        response, err := reader.ReadString('

        if len(response) >= minLen {
            return strings.TrimSpace(response)
        } else {
            fmt.Printf("Provide at least %d character.
", minLen)

Can you please help me to find out what's going wrong? Thanks a lot!

Adding setStdin as requested

func setStdin(userInput string) *os.File {
    tmpfile, err := ioutil.TempFile("", "test_stdin_")

    _, err = tmpfile.Write([]byte(userInput))
    _, err = tmpfile.Seek(0, 0)

    return tmpfile
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • douyigua5381 2018-08-06 07:28

    It pretty much looks like in your app your call Ask() whenever you want a single input line.

    Inside Ask() you create a bufio.Reader to read from os.Stdin. Know that bufio.Reader–as its name suggests–uses buffered reading, meaning it may read more data from its source than what is returned by its methods (Reader.ReadString() in this case). Which means if you just use it to read one (or some) lines and you throw away the reader, you will throw away buffered, unread data.

    So next time you call Ask() again, attempting to read from os.Stdin, you will not continue from where you left off...

    To fix this issue, only create a single bufio.Reader from os.Stdin, store it in a global variable for example, and inside Ask(), always use this single reader. So buffered and unread data will not be lost between Ask() calls. Of course this solution will not be valid to call from multiple goroutines, but reading from a single os.Stdin isn't either.

    For example:

    var reader = bufio.NewReader(os.Stdin)
    func Ask(question string, minLen int) string {
        // use the global reader here...

    Also note that using bufio.Scanner would be easier in your case. But again, bufio.Scanner may also read more data from its source than needed, so you have to use a shared bufio.Scanner here too. Also note that Reader.ReadString() returns you a string containing the delimeter (a line ending with in your case) which you probably have to trim, while Scanner.Text() (with the default line splitting function) will strip that first before returning the line. That's also a simplification you can take advantage of.

    打赏 评论

相关推荐 更多相似问题