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

huangapple go评论85阅读模式
英文:

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

问题

我有一个类似于这样的go测试代码:

func TestRollback(t *testing.T) {

  store := NewStore()
  
  // 做一些操作

  err := store.Rollback()
  
  // 检查
}

问题是store.Rollback()会从标准输入(stdin)读取一个提示,要求输入"y"或"n"。

当运行go test -v --run TestRollback时,如何向测试进程发送"y"呢?

英文:

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

答案1

得分: 2

你的Rollback方法测试的困难在于它在单例os.Stdin上硬编码了依赖关系。
Tinkerer的答案是可行的,但是因为它改变了那个包级变量,所以不适合并行运行测试。

一个更可取的替代方案(在我看来)是使用接口。在Go语言中,测试通常与接口相关。在这里,因为os.Stdin满足io.Reader接口,你可以在工厂函数中将io.Reader作为参数传递给你的Store类型:

type Store struct {
  // 其他字段,在此省略
  in io.Reader
}

func NewStore(in io.Reader) *Store {
  store := Store {
    // 其他字段,在此省略
    in: in,
  }
  return &store
}

然后,在你的测试函数中,你可以使用满足io.Reader接口且易于配置的具体类型,比如*strings.Reader

func TestRollback(t *testing.T) {
  // 准备
  in := strings.NewReader("-- 在这里放入stdin的内容 --")
  store := NewStore(in)
  // 执行
  err := store.Rollback()
  // 断言
  // ...
}
英文:

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
  // ...
}

答案2

得分: 1

以下可以临时重定向标准输入。

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

... 测试代码将数据写入 wr ...

os.Stdin = saved
英文:

The following can redirect stdin temporarily.

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

... Test code feeds wr ...

os.Stdin = saved

huangapple
  • 本文由 发表于 2021年7月20日 13:31:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/68449962.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定