如何模拟embed.FS?

问题描述 投票:0回答:1

我有一个模块 a.go,它从一堆文本文件中读取数据:

//go:embed *.txt
var textFiles embed.FS

func DoSomething() fs.FS {
    fmt.Println(textFiles)
}

在其他模块中,我在另一个函数中使用 DoSomething() ,如下所示:

func OtherF() {
    DoSomething()
}

我需要测试

OtherF
,但为此我需要模拟
textFiles
的内容 - 如何实现这一点?

我尝试替换包变量,但它是私有的,所以它不起作用。

go
1个回答
0
投票

在内存中模拟模拟文件系统进行测试的一种简单方法是使用

(testing/fstest).MapFS
,它实现了
fs.FS
文件系统接口。一种常见的方法是在测试执行期间简单地用符合相同接口的存根实现替换具体值。

但是,不幸的是,您不能直接使用

fs.FS
作为用
//go:embed
指令注释的变量的声明类型;这样做会导致编译时错误。尝试编译:

//go:embed mydir/*
var data fs.FS

产生以下编译器错误:

./mockfs.go:9:5: go:embed cannot apply to var of type fs.FS

作为此问题的解决方法,一种常见的方法是通过可替换的函数间接访问

embed.FS
或存根
fs.FS

  1. 隐藏对
    embed.FS
    的访问,该函数将
    embed.FS
    类型转换为
    fs.FS
    (很简单,因为前者实现了后者)
  2. 每当在嵌入式(或存根)文件系统中执行查找时,使用该函数检索目标文件系统的句柄
  3. 安排函数在测试中运行时返回模拟的文件系统,通过交换或检测测试框架的存在,并做出相应的响应。

这里是一些示例代码,可以通过交换函数来实现此目的:

//go:embed mydir/*
var data embed.FS

// getFS can be swapped at runtime to stub out a file system
// for test purposes.
var getFS = func() fs.FS {
    return data
}

并且,在您的测试用例中,您可以暂时用模拟的文件系统交换

getFS
(假设您的测试位于同一个包中):

func MyTest(t *testing.T) {
    origFS := getFS
    getFS = func() fs.FS {
        return fstest.MapFS{
            "mydir/someFile.txt": &fstest.MapFile{},
        }
    }
    t.Cleanup(func() {
        getFS = origFS
    })

    // your test logic here...
}
Go 游乐场中的

工作示例 - 但是,请注意,在此示例中,

//go:embed
指令已被破坏,因为游乐场似乎不支持包含附加文件(以允许解析
embed
)和执行嵌入式测试用例。如果您使用
mydir/
子目录中的一些示例数据运行此代码,则它应该可以在本地运行。


其他方法也是可能的,如果它们在测试期间交换行为(例如环境变量)或Go测试框架的检测。下面的代码示例执行后者,尽管存在(轻微)缺点,即模拟的文件系统定义在代码中与测试它的测试用例分开。

func getFS() fs.FS {
    if flag.Lookup("test.v") == nil {
        return data
    } else {
        return fstest.MapFS{
            "mydir/someFile.txt": &fstest.MapFile{},
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.