[英]How do I unit test this promptui package written in golang?
我是 golang 的新手,我在我的一个项目中使用了一个名为 promptui ( https://github.com/manifoldco/promptui ) 的交互式提示。 我已经为这个项目编写了几个单元测试,但我正在努力解决如何对这个需要输入的特定 package 进行单元测试的问题。
例如,我 go 如何测试以下代码行(封装在一个函数中):
func setEmail() string {
prompt := promptui.Prompt{Label: "Input your Email",
Validate: emailValidations,
}
email, err := prompt.Run()
if err != nil {
color.red("failed getting email")
os.exit(3)
}
return email
}
我想我需要以某种方式模拟 stdin 但无法在测试中找到最好的方法。
您不应该尝试测试promptui
因为它应该由作者测试。
你可以测试什么:
promptui.Prompt
时发送正确的参数 promptui.Prompt
promptui.Prompt
结果 如您所见,所有这些测试都不验证promptui.Prompt
是否在内部正常工作。
可以组合测试#2和#3。 您需要针对模拟运行代码,如果结果正确,您可以相信#2和#3都是正确的。
创建模拟:
type Runner interface {
Run() (int, string, error)
}
type promptMock struct {
// t is not required for this test, but it is would be helpful to assert input parameters if we have it in Run()
t *testing.T
}
func (p promptMock) Run() (int, string, error) {
// return expected result
return 1, "", nil
}
您将需要单独的模拟来测试错误流。
更新你的代码以注入mock:
func setEmail(runner Runner) string {
email, err := runner.Run()
if err != nil {
color.red("failed getting email")
os.exit(3)
}
return email
}
现在它是可测试的。
创建创建prompt
函数:
func getRunner() promptui.Prompt {
return promptui.Prompt{Label: "Input your Email",
Validate: emailValidations,
}
}
编写简单的断言测试以验证我们是否创建了正确的结构。
唯一没有测试的行将是setEmail(getRunner())
但它很简单,可以被其他类型的测试覆盖。
promptui 现在具有 Stdin 属性。
这里有一个小提琴: https://play.golang.org/p/-mSgjY2kAw-
这是我们将要测试的 function:
func mock(p promptui.Prompt) string {
p.Label = "[Y/N]"
user_input, err := p.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
}
return user_input
}
我们需要创建p
,它将是 promptui.Prompt 的一个实例并具有自定义标准输入。
我在这里得到了一些帮助 - https://groups.google.com/g/golang-nuts/c/J-Y4LtdGNSw?pli=1 - 如何制作自定义 Stdin 值,它只需要符合 io.ReadCloser .
type ClosingBuffer struct {
*bytes.Buffer
}
func (cb ClosingBuffer) Close() error {
return nil
}
然后在阅读器中将其用作 Stdin:
func TestMock(t *testing.T) {
reader := ClosingBuffer{
bytes.NewBufferString("N\n"),
}
p := promptui.Prompt{
Stdin: reader,
}
response := mock(p)
if !strings.EqualFold(response, "N") {
t.Errorf("nope!")
}
//t.Errorf(response)
}
编辑:以上内容不适用于同一个 function 中的多个提示,正如此处讨论的解决方案: https://github.com/manifoldco/promptui/issues/63 - “promptui 内部使用 4096 字节的缓冲区。这意味着您必须填充缓冲区,否则 promptui 将引发 EOF。”
我从那个交易所拿了这个pad()
function - https://github.com/sandokandias/capiroto/blob/master/cmd/capiroto/main.go :
func pad(siz int, buf *bytes.Buffer) {
pu := make([]byte, 4096-siz)
for i := 0; i < 4096-siz; i++ {
pu[i] = 97
}
buf.Write(pu)
}
然后测试——这个解决方案使用 ioutil.NopCloser 而不是创建一个新的结构:
func TestMock(t *testing.T) {
i1 := "N\n"
i2 := "Y\n"
b := bytes.NewBuffer([]byte(i1))
pad(len(i1), b)
reader := ioutil.NopCloser(
b,
)
b.WriteString(i2)
pad(len(i2), b)
p := promptui.Prompt{
Stdin: reader,
}
response := mock(p)
if !strings.EqualFold(response, "NY") {
t.Errorf("nope!")
t.Errorf(response)
}
}
和我们正在测试的 function:
func mock(p promptui.Prompt) string {
p.Label = "[Y/N]"
user_input, err := p.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
}
user_input2, err := p.Run()
return user_input + user_input2
}
多个提示的小提琴在这里: https://play.golang.org/p/ElPysYq8aM1
无论出于何种原因,他们不会导出他们的stdin
界面( https://github.com/manifoldco/promptui/blob/master/prompt.go#L49 ),所以你不能嘲笑它,但你可以直接嘲笑os.Stdin
并预先os.Stdin
测试所需的一切。 虽然我同意@Adrian,但它有自己的测试,所以这不应该是必要的。
从源中提取和重构/简化: 填写os.Stdin以获取从中读取的函数
以这种方式重构,它可以用于从os.Stdin
读取并期望特定字符串的任何函数。
游乐场链接: https : //play.golang.org/p/rjgcGIaftBK
func TestSetEmail(t *testing.T) {
if err := TestExpectedStdinFunc("email@test.com", setEmail); err != nil {
t.Error(err)
return
}
fmt.Println("success")
}
func TestExpectedStdinFunc(expected string, f func() string) error {
content := []byte(expected)
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
return err
}
defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write(content); err != nil {
return err
}
if _, err := tmpfile.Seek(0, 0); err != nil {
return err
}
oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }() // Restore original Stdin
os.Stdin = tmpfile
actual := f()
if actual != expected {
return errors.New(fmt.Sprintf("test failed, exptected: %s actual: %s", expected, actual))
}
if err := tmpfile.Close(); err != nil {
return err
}
return nil
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.