gpt4 book ai didi

unit-testing - 在 Golang 中测试文件系统的示例代码

转载 作者:IT王子 更新时间:2023-10-29 01:26:31 25 4
gpt4 key购买 nike

我正在尝试为将与文件系统交互的函数编写单元测试,我希望能够在测试期间模拟文件系统。

下面的代码是对 this 的回答。问题,您将在哪里创建一个文件系统接口(interface)以在测试期间使用,但我是 Go 的新手并且正在努力弄清楚如何使用它。

请问有人能举个例子说明如何在测试中使用这个接口(interface)吗?

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) }

最佳答案

你不能忘记一件重要的事情:如果与文件系统交互的代码通过上面提供的文件系统接口(interface)(filesystem),你只能模拟文件系统,使用 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(或任何接口(interface)),我们将在我们的 mockedFS 中嵌入 filesystem,因此我们“继承”了它的所有方法,并且我们只需要模拟可测试代码实际使用的内容。请注意,调用其他方法会导致运行时 panic ,因为我们不会真正为这个嵌入式 filesystem 提供一个合理的、非 nil 值,但为了测试不需要。

由于 filesystem 返回值 os.FileInfo (除了一个错误),它是一个接口(interface)(并且它的实现不是从 os 包中导出的),我们还需要模拟 os.FileInfo。这将是 mockedFileInfo,我们将非常类似于模拟 filesystem:我们将嵌入接口(interface)类型 os.FileInfo,所以实际上我们只需要实现 FileInfo.Size(),因为这是可测试的 getSize() 函数调用的唯一方法。

准备/设置模拟文件系统

一旦我们有了模拟类型,我们就必须设置它们。由于 getSize() 使用全局 fs 变量与文件系统交互,我们需要将我们的 mockedFS 的值分配给这个全局 fs 变量。在这样做之前,建议保存其旧值,并在完成测试后正确恢复旧值:“清理”。

由于我们完全想测试 getSize()(包括错误情况),我们为我们的 mockedFS 配备了控制它是否应该返回错误的能力,并且还能够告诉它返回什么,以防我们不希望出现任何错误。

在进行测试时,我们可以操纵 mockedFS 的“状态”,使其行为适应我们的需要。

和测试(ing)代码

废话不多说,完整测试代码:

type mockedFS struct {
// Embed so we only need to "override" what is used by testable functions
osFS

reportErr bool // Tells if this mocked FS should return error in our tests
reportSize int64 // Tells what size should Stat() report in our test
}

type mockedFileInfo struct {
// Embed this so we only need to add methods used by testable functions
os.FileInfo
size int64
}

func (m mockedFileInfo) Size() int64 { return m.size }

func (m mockedFS) Stat(name string) (os.FileInfo, error) {
if m.reportErr {
return nil, os.ErrNotExist
}
return mockedFileInfo{size: m.reportSize}, nil
}

func TestGetSize(t *testing.T) {
oldFs := fs
// Create and "install" mocked fs:
mfs := &mockedFS{}
fs = mfs
// Make sure fs is restored after this test:
defer func() {
fs = oldFs
}()

// Test when filesystem.Stat() reports error:
mfs.reportErr = true
if _, err := getSize("hello.go"); err == nil {
t.Error("Expected error, but err is nil!")
}

// Test when no error and size is returned:
mfs.reportErr = false
mfs.reportSize = 123
if size, err := getSize("hello.go"); err != nil {
t.Errorf("Expected no error, got: %v", err)
} else if size != 123 {
t.Errorf("Expected size %d, got: %d", 123, size)
}
}

关于unit-testing - 在 Golang 中测试文件系统的示例代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43912124/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com