Golang测试临时目录

21

我有一个简单的函数,可以将配置文件解析为JSON格式。我想写一个测试来使用一些静态配置文件并对其进行解析,或者在测试期间创建样本并尝试对其进行解析。

这个问题并不是很重要,但以下是基本代码:

// config.go

// ...(package,imports)...

// Overall settings - corresponds to main.conf
type MainSettings struct {
    // stuff
}

// Load main.conf from the specified file path
func LoadMainSettings(path string) (*MainSettings, error) {

    b, err := ioutil.ReadFile(path)
    if err != nil { return nil, err }

    r := &MainSettings{}
    err = json.Unmarshal(b, r)
    if err != nil { return nil, err }

    return r, nil

}

并且测试:

// config_test.go

func TestLoadMainSettings(t *testing.T) {

    // possibly generate some example config files,
    // or use static samples packaged with the source

    s, err := LoadMainSettings("conf/main.conf") // <-- what should this path be??
    if err != nil { panic(err) }

    // more sanity checking...

}

话虽如此,我的具体问题是:

  • 是否有适当的位置存放静态资源(例如示例配置文件),这些资源仅适用于测试?
  • 在测试执行期间,是否有一个适当的(跨平台、随“go clean”清理)位置来写出临时文件?

(注意:我在 Linux 上运行大部分内容进行暂存和生产,并在本地开发时使用 Mac - 因此,在实践中,将 /tmp/ 作为测试的临时目录对我有效。但我想知道是否有更好的方法...)


编辑: 最终采用了这种方法进行测试:

f, err := ioutil.TempFile("", "testmainconf")
if err != nil { panic(err) }
defer syscall.Unlink(f.Name())
ioutil.WriteFile(f.Name(), []byte("{...sample config data...}"), 0644)

s, err := LoadMainSettings(f.Name())

另一个建议是将LoadMainSettings接受io.Reader而不是string,这个建议也很好。


2
你能否编写测试代码,使其直接使用 io.Reader?如果可以的话,那么你的测试用例就不需要依赖文件系统了,因为你可以在测试代码中使用 strings.NewReader 来提供适当的测试内容。 - dyoo
嗯,这是一个有趣的想法... 对于调用者来说可能会稍微麻烦一些,但除此之外,在这种情况下它是可行的。 - Brad Peabody
1
调用者不应该更加繁琐。一个文件已经是一个 io.Reader - Tyler
没错,True是布尔类型,但字符串不是,我明白你的意思。我同意这是一个好主意。 - Brad Peabody
1
只是为了记录,如果使用带有路径参数的函数更方便,可以编写一个带有路径参数的公共函数,并从其中调用一个带有io.Reader(打开文件)的私有函数。然后仅测试内部函数。另一个只是一个包装器,它所做的就是编译“已测试”。 - zupa
5个回答

33
自从 Go1.15 版本开始,标准 testing 包中就有了 `T.TempDir()`。该方法会返回一个临时目录供测试使用。当测试及其子测试全部完成后,Cleanup 将自动删除该目录。每次调用 t.TempDir 都会返回一个唯一的目录。如果创建目录失败,则 TempDir 会通过调用 Fatal 终止测试。更多信息请参考文档:传送门

14

很酷 - 是的,那就是我最终做的。 - Brad Peabody

10

只是为了和 ioutil.TempDir 的情况进行比较,这里展示一下使用 io.Reader 的情况:

// Load main.conf from the specified file path
func LoadMainSettings(src io.Reader) (*MainSettings, error) {
    b, err := ioutil.ReadAll(src)
    if err != nil { return nil, err }

    r := &MainSettings{}
    err = json.Unmarshal(b, r)
    if err != nil { return nil, err }

    return r, nil
}

具体而言,我们将参数从path字符串更改为srcio.Reader实例,并使用ioutil.ReadAll替换ioutil.ReadFile

由于我们可以省略文件操作,因此您编写的测试用例会变得更短:

s, err := LoadMainSettings(strings.NewReader("{...sample config data...}"))

对于测试来说,这是正确的。这也意味着在主程序中调用者将会执行以下操作:f,err:= os.Open(“conf / main.conf”);/检查文件打开错误/defer f.close();s,err:= LoadMainSettings(f);如果配置始终从文件中读取,并且我有几种不同类型的配置(LoadSomeOtherSettings(),LoadYetAnotherConfig()等),我认为这种方法可能不是最好的选择。除非我漏掉了什么…… - Brad Peabody
还有一件事:我想我是在将这个与我的Java经验进行比较,通常情况下,在JUnit测试套件执行期间会创建一个输出目录,它通常不仅包含测试结果,还包括可以放置测试相关项的临时目录。当在构建上运行“clean”时,您知道它将全部消失。这使得事情变得简单。只是想知道是否在go中存在相应的概念-但正如我所看到的(并且你指出的),最好还是以不同的方式解决问题。 - Brad Peabody
恩。临时文件也没问题。您可以在像 http://golang.org/src/pkg/net/testdata/ 这样的软件包的“testdata”子目录中看到它们。所以这里没有硬性规定:有时将测试数据放在文件中是有意义的,有时将其嵌入测试中是有意义的。就像在 Java 中(您可以在各个地方看到 java.io.InputStreamjava.io.Reader),您会发现大多数 Go 代码都使用通用的 io.Reader,因此我只是想确保在某些地方将其记录为处理您的问题的方法。:P - dyoo

0

你也可以做到这一点:

uniqueTempDir, err := os.MkdirTemp(os.TempDir(), "*-myOptionalSuffix")
if err != nil {
    return err
}

0
你可以这样做:
go env -w GOTMPDIR="C:\Users<user>\go\tmp"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接