一尘不染

填充os.Stdin以读取其中的函数

go

如何在我的测试中填写os.Stdin中使用扫描仪从中读取的功能?

我要求使用以下功能通过扫描仪输入用户命令行:

func userInput() error {
    scanner := bufio.NewScanner(os.Stdin)

    println("What is your name?")
    scanner.Scan()
    username = scanner.Text()

    /* ... */
}

现在如何测试这种情况并模拟用户输入?以下示例不起作用。Stdin还是空的。

func TestUserInput(t *testing.T) {
    var file *os.File
    file.Write([]byte("Tom"))
    os.Stdin = file

    err := userInput()
    /* ... */
}

阅读 689

收藏
2020-07-02

共1个答案

一尘不染

嘲笑 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

简单,首选的方式

另请注意,您可以重构userInput()为不读取os.Stdin,但是可以接收io.Reader到读取的内容。这将使它更健壮并且更容易测试。

在您的应用程序,你可以简单地传递os.Stdin给它,并在测试中,你可以通过任何io.Reader它创建/在测试准备,例如使用strings.NewReader()bytes.NewBuffer()bytes.NewBufferString()

2020-07-02