Search code examples
gogo-testing

GO attach stdin in test setup method


I am using the built-in testing module to run some functional tests I have in my GO project. In my project I have external dependencies, which I connect to in my TestMain method. I save these connections to variables which then I use in the tests themselves, and the connections can take a long time to establish properly (Kafka anyone?). I would like to run the tests on-demand, but after these variables have been setup.

So what I want is to listen to stdin in my TestMain function and either run or quit the tests. But I want it to be controlled by the user so I can have my test environment setup, and the tests will run on my command.

But sadly, it seems that when running go test ... that stdin is mapped directly to /dev/null. So when I try to read os.Stdin I get an EOF error. The minimum code for this is:

package tests

import (
    "bufio"
    "fmt"
    "os"
    "testing"
)

func TestMain(m *testing.M) {
    reader := bufio.NewReader(os.Stdin)
    if input, err := reader.ReadString('\n'); err != nil {
        fmt.Println(err)
        fmt.Println("-----------")
        fmt.Println(input)
        os.Exit(1)
    }
    fmt.Println("ESCAPED!")
    os.Exit(m.Run())
}

I have read in circles how to mock this for unit tests and the sort, but my case is more of a functional test runner. Is there a way, or even some trickery, that will allow me to open or change the test processes stdin?


Solution

  • You can redirect os.Stdin, it depends on OS though:

    package tests
    
    import (
        "fmt"
            "os"
            "testing"
        "bufio"
        "runtime"
    )
    
    func TestMain(m *testing.M) {
    
        var ttyName string
        if runtime.GOOS == "windows" {
            ttyName = "con"
        } else {
            ttyName = "/dev/tty"
        }   
    
        f, err := os.Open(ttyName)
        if err != nil {
            panic(err)
        }
        defer f.Close()
    
        oldStdin := os.Stdin
        defer func() { os.Stdin = oldStdin }()
    
        os.Stdin = f
    
        reader := bufio.NewReader(os.Stdin)
            if input, err := reader.ReadString('\n'); err != nil {
            fmt.Println("Error:", err)
            fmt.Println("-----------")
            fmt.Println(input)
            os.Exit(1)
        }
        fmt.Println("ESCAPED!")
        os.Exit(m.Run())
    }
    
    func TestHello(t *testing.T){
        fmt.Println("Hello")
    }