我正在尝试为与文件系统交互的函数编写单元测试,我希望能够在测试期间模拟文件系统。
下面的代码是this问题的答案,在那里您将创建一个在测试期间使用的文件系统接口,但我是Go的新手,并且正在努力弄清楚如何使用它。 / p>
有人能够提供一个如何在测试中使用此接口的示例吗?
var fs fileSystem = osFS{}
type fileSystem interface {
Open(name string) (file, error)
Stat(name string) (os.FileInfo, error)
}
type file interface {
io.Closer
io.Reader
io.ReaderAt
io.Seeker
Stat() (os.FileInfo, error)
}
// osFS implements fileSystem using the local disk.
type osFS struct{}
func (osFS) Open(name string) (file, error) { return os.Open(name) }
func (osFS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
答案 0 :(得分:4)
您不能忘记的一件重要事情:如果与文件系统交互的代码通过上面提供的文件系统接口(filesystem
)使用{{1}进行,则只能模拟文件系统全局变量(或测试代码可以更改的其他fs
值,例如传递的filesystem
参数)。
让我们看看这样一个示例函数:
fs
这个简单的func getSize(name string) (int64, error) {
stat, err := fs.Stat(name)
if err != nil {
return 0, err
}
return stat.Size(), nil
}
函数返回由其名称指定的文件的大小,如果getSize()
失败则返回错误(返回错误)。
现在让我们编写一些完全涵盖此filesystem.Stat()
函数的单元测试。
我们需要一个模拟的getSize()
版本,进行模拟,以便它实际上不与文件系统交互,但在调用filesystem
的方法时返回合理数据(filesystem
在我们的例子中)。为了最简单的模拟filesystem.Stat()
(或任何界面),我们会在filesystem
中嵌入filesystem
,因此我们“继承”其所有方法,我们只需要模拟实际使用的内容通过可测试的代码。请注意,调用其他方法会导致运行时出现混乱,因为我们不会给这个嵌入式mockedFS
提供合理的非nil
值,但是为了测试它不需要。 / p>
由于filesystem
返回值os.FileInfo
(除了错误),这是一个接口(并且它的实现不是从os
包导出的),我们还需要模拟filesystem
。这将是os.FileInfo
,我们将非常类似于模拟mockedFileInfo
:我们将嵌入接口类型filesystem
,所以实际上我们只需要实现{{1} },因为这是testable os.FileInfo
函数调用的唯一方法。
一旦我们有了模拟类型,我们就必须设置它们。由于FileInfo.Size()
使用全局getSize()
变量与文件系统进行交互,因此我们需要将getSize()
的值分配给此全局fs
变量。在这样做之前,建议保存旧值,并在完成测试后正确恢复旧值:“cleanup”。
由于我们完全想要测试mockedFS
(包括错误情况),我们为我们的fs
提供了控制是否应该返回错误的能力,并且能够告诉它如果我们不想要任何错误,请返回。
在进行测试时,我们可以操纵getSize()
的“状态”,使其行为符合我们的需要。
不用多说,完整的测试代码:
mockedFS
答案 1 :(得分:1)
另一个选项是 testing/fstest
包:
package main
import "testing/fstest"
func main() {
m := fstest.MapFS{
"hello.txt": {
Data: []byte("hello, world"),
},
}
b, e := m.ReadFile("hello.txt")
if e != nil {
panic(e)
}
println(string(b) == "hello, world")
}