【问题标题】:How do I write a go Test function for something that reads from stdin?如何为从标准输入读取的内容编写 go Test 函数?
【发布时间】:2021-09-27 16:13:34
【问题描述】:

我已经去测试类似这样的代码:

func TestRollback(t *testing.T) {

  store := NewStore()
  
  // do some stuff

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

问题是 store.Rollback() 提示从标准输入读取 y 或 n

运行go test -v --run TestRollback时如何向测试进程发送“y”

【问题讨论】:

    标签: go testing mocking stdin


    【解决方案1】:

    测试Rollback 方法的困难在于硬编码它对单例os.Stdin 的依赖。 Tinkerer's answer 是可行的,但是因为它改变了包级变量,所以它不适合并行运行测试。

    一个更可取的替代方案 (IMO) 在于使用接口。在 Go 中,测试通常与接口押韵。在这里,因为 os.Stdin 满足 io.Reader 接口,您可以使用传递给您的工厂函数的 io.Reader 参数化您的 Store 类型:

    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
    }
    

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

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

    【讨论】:

    • 谢谢,这是一个合理的建议。但我决定换个方向,只是在 Rollback 中添加了一个 bool 参数,它会跳过提示并选择“y”来继续执行 Rollback 任务。
    【解决方案2】:

    以下可以临时重定向stdin。

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

    【讨论】:

    • 虽然这可行,但由于其他答案指出的原因,我决定我不喜欢它。所以我修改了 Rollback 函数以使用 bool 来关闭提示。我认为这更整洁。
    猜你喜欢
    • 1970-01-01
    • 2020-03-07
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 2015-04-20
    相关资源
    最近更新 更多