Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I write a Go test that writes to stdin?

Say that I have a simple application that reads lines from stdin and simply echoes it back to stdout. For example:

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    for {
        fmt.Print("> ")
        bytes, _, err := reader.ReadLine()
        if err == io.EOF {
            os.Exit(0)
        }
        fmt.Println(string(bytes))
    }
}

I would like to write a test case that writes to stdin and then compares the output to the input. For example:

package main

import (
    "bufio"
    "io"
    "os"
    "os/exec"
    "testing"
)

func TestInput(t *testing.T) {
    subproc := exec.Command(os.Args[0])
    stdin, _ := subproc.StdinPipe()
    stdout, _ := subproc.StdoutPipe()
    defer stdin.Close()

    input := "abc\n"

    subproc.Start()
    io.WriteString(stdin, input)
    reader := bufio.NewReader(stdout)
    bytes, _, _ := reader.ReadLine()
    output := string(bytes)
    if input != output {
        t.Errorf("Wanted: %v, Got: %v", input, output)
    }
    subproc.Wait()
}

Running go test -v gives me the following:

=== RUN   TestInput
--- FAIL: TestInput (3.32s)
    echo_test.go:25: Wanted: abc
        , Got: --- FAIL: TestInput (3.32s)
FAIL
exit status 1

I'm obviously doing something incorrect here. How should I go about testing this type of code?

like image 945
Del Putnam Avatar asked May 16 '16 14:05

Del Putnam


2 Answers

Instead of doing everything in main with stdin and stdout, you can define a function that takes an io.Reader and an io.Writer as parameters and does whatever you want it to do. main could then call that function and your test function could test that function directly.

like image 146
Andy Schweig Avatar answered Sep 18 '22 01:09

Andy Schweig


Here is an example that writes to stdin and reads from stdout. Note that it does not work because the output contains "> " at first. Still, you can modify it to suit your needs.

func TestInput(t *testing.T) {
    subproc := exec.Command("yourCmd")
    input := "abc\n"
    subproc.Stdin = strings.NewReader(input)
    output, _ := subproc.Output()

    if input != string(output) {
        t.Errorf("Wanted: %v, Got: %v", input, string(output))
    }
    subproc.Wait()
}
like image 36
T. Claverie Avatar answered Sep 22 '22 01:09

T. Claverie