How do I write a go Test function for something that reads from stdin?

Viewed 283

I have go test code similar to this:

func TestRollback(t *testing.T) {

  store := NewStore()
  
  // do some stuff

  err := store.Rollback()
  
  // checks
}

The problem is store.Rollback() has a prompt read from the stdin for y or n

How do I send "y" to the test process when running go test -v --run TestRollback

2 Answers

The difficulty in testing your Rollback method stems from hardcoding its dependency on singleton os.Stdin. Tinkerer's answer is viable but, because it mutates that package-level variable, it doesn't lend itself to running tests in parallel.

A preferable alternative (IMO) consists in using an interface. Testing often rhymes with interface, in Go. Here, because os.Stdin satisfies the io.Reader interface, you could parameterise your Store type with an io.Reader passed to your factory function:

type Store struct {
  // other fields, omitted here
  in io.Reader
}

func NewStore(in io.Reader) *Store {
  store := Store {
    // other fields, omitted here
    in: in,
  }
  return &store
}

Then, in your test functions, you could use a concrete type that satisfies io.Reader and is easily configurable, such as a *strings.Reader:

func TestRollback(t *testing.T) {
  // arrange
  in := strings.Reader("-- put contents of stdin here --")
  store := NewStore(in)
  // act
  err := store.Rollback()
  // assert
  // ...
}

The following can redirect stdin temporarily.

rd,wr,err := os.Pipe()
saved := os.Stdin
os.Stdin = rd

... Test code feeds wr ...

os.Stdin = saved
Related