有没有人在golang.org/x/crypto/ssh/terminal包中模拟term.ReadPassword(int(os.Stdin.Fd()))调用的入口(出于测试目的)的最好方法是成功的,或者有什么想法?
我尝试创建一个临时文件(vs os.Stdin)并将testing\n或testing\r之类的字符串值写入临时文件,但得到错误inappropriate ioctl for device。我猜它是与TTY相关的东西或缺少的特定格式(?)但我真的不确定。
感谢你的帮助。
发布于 2016-07-26 15:01:00
如果您通过创建os.Stdin引用的假文件来破坏此测试,那么当您尝试处理ReadPassword()时,您的测试将变得非常特定于操作系统。这是因为在幕后,Go根据操作系统编译不同的syscall。ReadPassword()是用here实现的,而基于体系结构和操作系统的系统调用是用this directory实现的。如你所见,有很多种。我想不出一个好的方法来以您指定的方式来存根这个测试。
由于对您的问题的了解有限,我建议的解决方案是注入一个简单的界面,如下所示:
type PasswordReader interface {
ReadPassword(fd int) ([]byte, error)
}
func (pr PasswordReader) ReadPassword(fd int) ([]byte, error) {
return terminal.ReadPassword(fd)
}通过这种方式,您可以向测试传递一个假对象,并将响应存根到ReadPassword。我知道这感觉就像是为您的测试编写代码,但是您可以重新定义这种想法,因为terminal是一个应该注入的外部依赖(I/O)!因此,现在您的测试不仅可以确保代码正常工作,还可以帮助您做出良好的设计决策。
发布于 2019-01-14 22:17:17
科尔宾的例子促使我研究了界面模拟,并促使我写了一个基本的例子:
// 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输入。
// 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、证言和造假者,基本上为你做了所有繁重的工作,你可以在代码中添加生成器步骤:
//go:generate mockgen -destination=../mocks/mock_getter.go -package=mocks github.com/petems/passwordgetter/cmd PasswordReader然后包含在测试中生成的mock:
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)
}https://stackoverflow.com/questions/38573176
复制相似问题