首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模拟crypto/ssh/terminal

模拟crypto/ssh/terminal
EN

Stack Overflow用户
提问于 2016-07-26 00:36:41
回答 2查看 1.7K关注 0票数 3

有没有人在golang.org/x/crypto/ssh/terminal包中模拟term.ReadPassword(int(os.Stdin.Fd()))调用的入口(出于测试目的)的最好方法是成功的,或者有什么想法?

我尝试创建一个临时文件(vs os.Stdin)并将testing\ntesting\r之类的字符串值写入临时文件,但得到错误inappropriate ioctl for device。我猜它是与TTY相关的东西或缺少的特定格式(?)但我真的不确定。

感谢你的帮助。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-07-26 15:01:00

如果您通过创建os.Stdin引用的假文件来破坏此测试,那么当您尝试处理ReadPassword()时,您的测试将变得非常特定于操作系统。这是因为在幕后,Go根据操作系统编译不同的syscall。ReadPassword()是用here实现的,而基于体系结构和操作系统的系统调用是用this directory实现的。如你所见,有很多种。我想不出一个好的方法来以您指定的方式来存根这个测试。

由于对您的问题的了解有限,我建议的解决方案是注入一个简单的界面,如下所示:

代码语言:javascript
复制
type PasswordReader interface {
    ReadPassword(fd int) ([]byte, error)
}

func (pr PasswordReader) ReadPassword(fd int) ([]byte, error) {
    return terminal.ReadPassword(fd)
}

通过这种方式,您可以向测试传递一个假对象,并将响应存根到ReadPassword。我知道这感觉就像是为您的测试编写代码,但是您可以重新定义这种想法,因为terminal是一个应该注入的外部依赖(I/O)!因此,现在您的测试不仅可以确保代码正常工作,还可以帮助您做出良好的设计决策。

票数 4
EN

Stack Overflow用户

发布于 2019-01-14 22:17:17

科尔宾的例子促使我研究了界面模拟,并促使我写了一个基本的例子:

代码语言:javascript
复制
// getter.go
package cmd

import (
    "errors"
    "syscall"

    "golang.org/x/crypto/ssh/terminal"
)

type PasswordReader interface {
    ReadPassword() (string, error)
}

type StdInPasswordReader struct {
}

func (pr StdInPasswordReader) ReadPassword() (string, error) {
    pwd, error := terminal.ReadPassword(int(syscall.Stdin))
    return string(pwd), error
}

func readPassword(pr PasswordReader) (string, error) {
    pwd, err := pr.ReadPassword()
    if err != nil {
        return "", err
    }
    if len(pwd) == 0 {
        return "", errors.New("empty password provided")
    }
    return pwd, nil
}

func Run(pr PasswordReader) (string, error) {
    pwd, err := readPassword(pr)
    if err != nil {
        return "", err
    }

    return string(pwd), nil
}

在测试中,我们可以模拟错误并模拟没有stdin输入。

代码语言:javascript
复制
// getter_test.go
package cmd_test
import (
    "errors"
    "testing"

    "github.com/petems/passwordgetter/cmd"
    "github.com/stretchr/testify/assert"
)

type stubPasswordReader struct {
    Password    string
    ReturnError bool
}

func (pr stubPasswordReader) ReadPassword() (string, error) {
    if pr.ReturnError {
        return "", errors.New("stubbed error")
    }
    return pr.Password, nil
}

func TestRunReturnsErrorWhenReadPasswordFails(t *testing.T) {
    pr := stubPasswordReader{ReturnError: true}
    result, err := cmd.Run(pr)
    assert.Error(t, err)
    assert.Equal(t, errors.New("stubbed error"), err)
    assert.Equal(t, "", result)
}

func TestRunReturnsPasswordInput(t *testing.T) {
    pr := stubPasswordReader{Password: "password"}
    result, err := cmd.Run(pr)
    assert.NoError(t, err)
    assert.Equal(t, "password", result)
}

还有一些工具,如gomock、证言和造假者,基本上为你做了所有繁重的工作,你可以在代码中添加生成器步骤:

代码语言:javascript
复制
//go:generate mockgen -destination=../mocks/mock_getter.go -package=mocks github.com/petems/passwordgetter/cmd PasswordReader

然后包含在测试中生成的mock:

代码语言:javascript
复制
package cmd_test

import (
    "testing"
    "errors"

    "github.com/golang/mock/gomock"

    "github.com/petems/passwordgetter/mocks"
    "github.com/petems/passwordgetter/cmd"
    "github.com/stretchr/testify/assert"
)

func TestRunReturnsErrorWhenEmptyString(t *testing.T) {

    mockCtrl := gomock.NewController(t)
  defer mockCtrl.Finish()

  mockPasswordReader := mocks.NewMockPasswordReader(mockCtrl)

  mockPasswordReader.EXPECT().ReadPassword().Return("", nil).Times(1)

    result, err := cmd.Run(mockPasswordReader)
    assert.Error(t, err)
    assert.Equal(t, errors.New("empty password provided"), err)
    assert.Equal(t, "", result)
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38573176

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档