简体   繁体   English

如何在 Go 中测试嵌套输入

[英]How to test nested input in Go

I have function for getting user input using os.Stdin我有使用os.Stdin获取用户输入的功能

func (i input) GetInput(stdin io.Reader) (string, error) {
    reader := bufio.NewReader(stdin)

    data, err := reader.ReadString('\n')
    if err != nil {
        return "", fmt.Errorf("get input error: %w", err)
    }

    return strings.ReplaceAll(data, "\n", ""), nil
}

In my programm I need to have 2 inputs:在我的程序中,我需要有 2 个输入:

  • First for getting base info for example user name首先获取基本信息,例如用户名
  • Second for getting aditional info that depends on first input其次用于获取取决于第一个输入的附加信息
name,err := GetInput(os.Stdin)
if err != nil {
 // error handling.....
}

switch name {
  case "test":
    //do something...
    age, err := GetInput(os.Stdin)
    if err != nil {
      // error handling.....
    }
    fmt.Println(age)

  case "another":
    // Here another input
}

It it possible to write unit tests for that case?可以为这种情况编写单元测试吗? For testing one user input I use this snippet and it works:为了测试一个用户输入,我使用了这个代码片段并且它有效:

    var stdin bytes.Buffer
    stdin.Write([]byte(fmt.Sprintf("%s\n", tt.input)))
    GetInput(stdin)

But it didn't work with 2 nested inputs但它不适用于 2 个嵌套输入

Maybe consider having a function that returns a specific type as a result and put it into a separate package.也许考虑拥有一个返回特定类型作为结果的函数,并将其放入一个单独的包中。

Since I see name and age mentioned, perhaps we can assume a concrete type like Person for illustration.由于我看到提到了nameage ,也许我们可以假设一个具体的类型,例如Person来进行说明。

It is important to note that we want to include the actual reader as a parameter and not have a hard coded reference to os.Stdin .重要的是要注意,我们希望将实际的阅读器作为参数包含在内,而不是对os.Stdin进行硬编码引用。 This makes the mocking of nested inputs possible in the first place.这使得嵌套输入的模拟成为可能。

With this, the signature of the method could look something like the following:这样,该方法的签名可能类似于以下内容:

func NestedInput(input io.Reader) (*Person, error) 

The corresponding type could be:对应的类型可以是:

type Person struct {
    Name string
    Age  int
}

If one now combines your code snippets to a complete GO file with the name input.go in a separate directory, it might look something like this:如果现在将您的代码片段组合成一个完整的 GO 文件,名称为input.go ,位于单独的目录中,它可能看起来像这样:

package input

import (
    "bufio"
    "fmt"
    "io"
    "strconv"
    "strings"
)

func getInput(reader *bufio.Reader) (string, error) {
    data, err := reader.ReadString('\n')
    if err != nil {
        return "", fmt.Errorf("get input error: %w", err)
    }

    return strings.ReplaceAll(data, "\n", ""), nil
}

type Person struct {
    Name string
    Age  int
}

func NestedInput(input io.Reader) (*Person, error) {
    reader := bufio.NewReader(input)
    name, err := getInput(reader)
    if err != nil {
        return nil, err
    }

    switch name {
    case "q":
        return nil, nil
    default:
        ageStr, err := getInput(reader)
        if err != nil {
            return nil, err
        }
        age, err := strconv.Atoi(ageStr)
        if err != nil {
            return nil, err
        }
        return &Person{Name: name, Age: age}, nil
    }
}

An input of q returns nil, nil and could be used to terminate the input, eg if the query was made in a loop. q的输入返回nil, nil并可用于终止输入,例如,如果查询是在循环中进行的。

Unit Test单元测试

The unit test单元测试

func Test_nestedInput(t *testing.T)

in a file named input_test.go should now provide the input data.在名为input_test.go的文件中,现在应该提供输入数据。

Since the NestedInput function now expects an io.Reader as a parameter, we can simply generate the desired input with, for example,由于NestedInput函数现在需要一个io.Reader作为参数,我们可以简单地生成所需的输入,例如,

input := strings.NewReader("George\n26\n")

So the test could look something like this:所以测试看起来像这样:

package input

import (
    "strings"
    "testing"
)

func Test_nestedInput(t *testing.T) {
    input := strings.NewReader("George\n26\n")
    person, err := NestedInput(input)
    if err != nil {
        t.Error("nested input failed")
    }
    if person == nil {
        t.Errorf("expected person, but got nil")
        return
    }
    if person.Name != "George" {
        t.Errorf("wrong name %s, expected 'George'", person.Name)
    }
    if person.Age != 26 {
        t.Errorf("wrong age %d, expected 26", person.Age)
    }
}

Of course, the tests can be extended with further details.当然,可以通过进一步的细节来扩展测试。 But this, as you can see, mocks a nested input.但是,如您所见,这模拟了一个嵌套输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM