我正在尝试在Go中为此功能编写测试:
type LinuxBackend struct {
fs afero.Fs
}
func (b *LinuxBackend) GetSSHUser(name string) users.SSHUser {
// Get the user from the system
usr, err := user.Lookup(name)
if err != nil {
panic(err)
}
return users.SSHUser{ User: usr }
}
我想要一个假的/etc/passwd
,以便我的测试可以在该文件中创建必要的用户,而不是依赖于真实的系统文件。
对于代码的其他部分,我使用Afero抽象了基础文件系统,以创建内存中文件系统,并创建必要的文件(包括/etc/passwd
),以便可以运行测试。这就是fs
中LinuxBackend
字段的作用。
例如,这是同一包中的另一个函数:
func (b *LinuxBackend) GetMinMaxUid() (int, int) {
content, err := afero.ReadFile(b.fs, LoginDefsPath)
// more code here...
return min, max
}
然后我可以在不接触真实文件系统的情况下进行测试:
func createLoginDefs(afs afero.Fs) {
afs.MkdirAll("/etc", os.FileMode(os.ModeDir))
f, _ := afs.OpenFile(LoginDefsPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
f.WriteString("UID_MIN " + strconv.Itoa(uidMin) + "\n")
f.WriteString("UID_MAX " + strconv.Itoa(uidMax) + "\n")
}
func TestLinuxBackend_GetMinMaxUid(t *testing.T) {
afs := afero.NewMemMapFs() // <-- This is the trick.
createLoginDefs(afs)
backend := LinuxBackend{afs}
min, max := backend.GetMinMaxUid()
// rest of the test...
}
不过,对于user.Lookup()
,我无法做到这一点,这意味着它将尝试从真实文件系统中获取不存在的测试用户。
user.Lookup
调用user.lookupUser
,它具有两个具体的实现,一个pure Go和一个cgo one。我认为,后者完全不可能强迫使用虚拟文件系统,因为后者使用libc的pw*
实用程序功能来获取用户。前者通过Open
设置文件句柄开始,然后将其传递到以findUsername
作为参数的io.Reader
函数,但是该函数未从模块中导出,因此我可以不能直接使用它来将Reader从Afero文件系统传递给它。
有没有一种方法可以使Go使用user.Lookup
和user.lookupUser
的内存文件系统,或者以其他方式编写代码或测试,以便user.Lookup
可以不使用真实的文件系统?
我总是可以复制user.lookupUser
,但我宁愿避免这样做。