如何在我的测试中填充 os.Stdin 以获取使用扫描仪从中读取的函数?
我使用以下函数通过扫描器请求用户命令行输入:
func userInput() error {
scanner := bufio.NewScanner(os.Stdin)
println("What is your name?")
scanner.Scan()
username = scanner.Text()
/* ... */
}
现在如何测试这种情况并模拟用户输入? 以下示例不起作用。标准输入仍然是空的。
func TestUserInput(t *testing.T) {
var file *os.File
file.Write([]byte("Tom"))
os.Stdin = file
err := userInput()
/* ... */
}
最佳答案
模拟os.Stdin
os.Stdin
是一个您可以修改的变量(类型为 *os.File
),您在正确的轨道上,您可以在测试中为其分配新值。
最简单的方法是创建一个临时文件,其中包含您要模拟的内容作为 os.Stdin
的输入。要创建临时文件,请使用 ioutil.TempFile()
.然后将内容写入其中,并寻回文件开头。现在您可以将其设置为 os.Stdin
并执行您的测试。不要忘记清理临时文件。
我将你的 userInput()
修改为:
func userInput() error {
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("What is your name?")
var username string
if scanner.Scan() {
username = scanner.Text()
}
if err := scanner.Err(); err != nil {
return err
}
fmt.Println("Entered:", username)
return nil
}
这是您可以测试它的方式:
func TestUserInput(t *testing.T) {
content := []byte("Tom")
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write(content); err != nil {
log.Fatal(err)
}
if _, err := tmpfile.Seek(0, 0); err != nil {
log.Fatal(err)
}
oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }() // Restore original Stdin
os.Stdin = tmpfile
if err := userInput(); err != nil {
t.Errorf("userInput failed: %v", err)
}
if err := tmpfile.Close(); err != nil {
log.Fatal(err)
}
}
运行测试,我们看到输出:
What is your name?
Entered: Tom
PASS
另请参阅有关模拟文件系统的相关问题:Example code for testing the filesystem in Golang
简单的首选方式
另请注意,您可以重构 userInput()
使其不从 os.Stdin
读取,而是接收 io.Reader
从中读取。这将使它更健壮并且更容易测试。
在您的应用中,您可以简单地将 os.Stdin
传递给它,在测试中,您可以将任何 io.Reader
传递给它在测试中创建/准备的,例如使用 strings.NewReader()
, bytes.NewBuffer()
或 bytes.NewBufferString()
.
关于unit-testing - 为从中读取的函数填充 os.Stdin,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47139633/