如何在Go中测试嵌套输入

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

How to test nested input in Go

问题

我有一个使用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("获取输入错误:%w", err)
	}

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

在我的程序中,我需要有两个输入:

  • 第一个用于获取基本信息,例如用户名
  • 第二个用于获取依赖于第一个输入的附加信息
name, err := GetInput(os.Stdin)
if err != nil {
	// 错误处理...
}

switch name {
case "test":
	// 做一些事情...
	age, err := GetInput(os.Stdin)
	if err != nil {
		// 错误处理...
	}
	fmt.Println(age)

case "another":
	// 这里是另一个输入
}

对于这种情况,是否可以编写单元测试?
对于测试一个用户输入,我使用以下代码片段,它可以正常工作:

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

但是对于嵌套的两个输入,它不起作用。

英文:

I have function for getting user input using 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:

  • 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

答案1

得分: 1

也许考虑创建一个返回特定类型作为结果并将其放入单独的包中的函数。

由于我看到提到了nameage,也许我们可以假设一个具体的类型,比如Person来进行说明。

重要的是要注意,我们希望将实际的读取器作为参数包含进来,而不是硬编码引用os.Stdin。这样首先就可以模拟嵌套输入。

有了这个,方法的签名可能如下所示:

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

相应的类型可以是:

type Person struct {
	Name string
	Age  int
}

如果现在将你的代码片段组合成一个名为input.go的完整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("获取输入错误:%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
	}
}

输入q将返回nil, nil,可以用来终止输入,例如在循环中进行查询。

单元测试

现在,名为input_test.go的文件中的单元测试

func Test_nestedInput(t *testing.T)

应该提供输入数据。

由于NestedInput函数现在期望一个io.Reader作为参数,我们可以使用以下方式简单生成所需的输入:

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

因此,测试可能如下所示:

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("嵌套输入失败")
	}
	if person == nil {
		t.Errorf("期望得到person,但得到了nil")
		return
	}
	if person.Name != "George" {
		t.Errorf("错误的姓名%s,期望'George'", person.Name)
	}
	if person.Age != 26 {
		t.Errorf("错误的年龄%d,期望26", person.Age)
	}
}

当然,测试可以根据需要进行扩展。但是,正如你所看到的,这样模拟了一个嵌套输入。

英文:

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.

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. 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:

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, e.g. if the query was made in a loop.

Unit Test

The unit test

func Test_nestedInput(t *testing.T)

in a file named input_test.go should now provide the input data.

Since the NestedInput function now expects an io.Reader as a parameter, we can simply generate the desired input with, for example,

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.

huangapple
  • 本文由 发表于 2022年12月17日 23:57:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/74835444.html
匿名

发表评论

匿名网友

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

确定