测试和工作目录

我正在为 Go 中的应用程序编写一些单元测试。但是测试失败了,因为它找不到配置文件。通常情况下,二进制文件会在路径 abc0的工作目录中查找配置文件。

我认为浏览到包含 conf/的目录并在其中运行 go test可以解决这个问题,但是它仍然报告说文件系统找不到指定的路径。

如何告诉 go test使用某个目录作为工作目录,以便实际执行测试?

55563 次浏览

我不相信这是可能的。我还没有找到明确说明这一点的文档,但我相信 go test总是使用包目录(包含 go 源文件)作为工作目录。

虽然不太方便,但是您总是可以将其作为命令行变量传递,例如:

package blah_test


import (
"flag"
"fmt"
"os"
"testing"
)


var (
cwd_arg = flag.String("cwd", "", "set cwd")
)


func init() {
flag.Parse()
if *cwd_arg != "" {
if err := os.Chdir(*cwd_arg); err != nil {
fmt.Println("Chdir error:", err)
}
}
}


func TestBlah(t *testing.T) {
t.Errorf("cwd: %+q", *cwd_arg)
}

那就这么说:

┌─ oneofone@Oa [/tmp]
└──➜ go test . -cwd="$PWD"
--- FAIL: TestBlah (0.00 seconds)
blah_test.go:16: cwd: "/tmp"

您可以使用 Caller 获取当前测试源文件的路径,如下所示:

package sample


import (
"testing"
"runtime"
"fmt"
)


func TestGetFilename(t *testing.T) {
_, filename, _, _ := runtime.Caller(0)
t.Logf("Current test filename: %s", filename)
}

作为解决方案,我编译了测试并从工作目录执行了测试。

go test -c && ./<mypackage>.test

或者,如果希望使用通用命令,可以使用 -o选项重命名测试文件。

go test -c -o xyz.test && ./xyz.test

你可以使用 操作系统包

你会想这么做的

    func TestMyFunction(t *testing.T) {
os.Chdir("./path")


//TEST FUNCTION


os.Chdir("..")
}

在 OS 包中有几种可能性。

我会用一个环境变量来定位你的申请。这似乎是运行 go 工具的最佳方式,因为测试程序可以从临时位置运行。

// get home dir of app, use MYAPPHOME env var if present, else executable dir.
func exeDir() string {
dir, exists := os.LookupEnv("MYAPPHOME")
if exists {
return dir
} else {
ex, err := os.Executable()
if err != nil {
panic(err)
}
exPath := path.Dir(ex)
return exPath
}
}

无论工作目录在哪里。它必须在您的项目目录下。所以我的解决方案是

wd, _ := os.Getwd()
for !strings.HasSuffix(wd, "<yourProjectDirName>") {
wd = filepath.Dir(wd)
}


raw, err := ioutil.ReadFile(fmt.Sprintf("%s/src/conf/conf.dev.json", wd))

您的路径应该始终从项目 Dir 开始。每次读取包中的文件并由 main.go 或另一个包单元测试访问时。总会成功的。

我有一个类似的问题,并找到了解决方案 在这个博客上

基本上你可以用一个类似的函数来更改测试运行的文件夹:

package main


import (
"os"
"path"
"runtime"
)


func MakeFunctionRunOnRootFolder() {
_, filename, _, _ := runtime.Caller(0)
// The ".." may change depending on you folder structure
dir := path.Join(path.Dir(filename), "..")
err := os.Chdir(dir)
if err != nil {
panic(err)
}
}

要将 init 函数添加到 * _ test.go 在测试包下面。 测试包将在测试函数启动之前运行此函数。

func init() {
_, filename, _, _ := runtime.Caller(0)
// The ".." may change depending on you folder structure
dir := path.Join(path.Dir(filename), "..")
err := os.Chdir(dir)
if err != nil {
panic(err)
}
}

我知道这是一个老问题,但是我在测试中使用数据库迁移时遇到了同样的问题,也许这个解决方案对某些人有帮助。

由于没有获取项目目录的本机方法,所以您可以识别一些文件或目录,您知道它们只在项目的根目录中(在我的例子中,它是相对目录 数据库/迁移)。一旦您有了这个唯一的相对目录,您就可以使用下面这样的函数来获得项目根目录。它只是获取当前的工作目录(假设它在项目的目录中) ,然后开始一路向上导航,直到找到一个 dir,它有一个相对目录,你知道它在项目的根目录中:

func FindMyRootDir() string {
workingDirectory, err := os.Getwd()


if err != nil {
panic(err)
}


lastDir := workingDirectory
myUniqueRelativePath := "database/migrations"


for {
currentPath := fmt.Sprintf("%s/%s", lastDir, myUniqueRelativePath)


fi, err := os.Stat(currentPath)


if err == nil {
switch mode := fi.Mode(); {
case mode.IsDir():
return currentPath
}
}


newDir := filepath.Dir(lastDir)


// Ooops, we couldn't find the root dir. Check that your "myUniqueRelativePath" really exists


if newDir == "/" || newDir == lastDir {
return ""
}


lastDir = newDir
}
}

当然这不是最漂亮的解决方案,但是很有效。

在 Go 中将测试夹具 在同一个包裹里放在 testdata文件夹中是一种常见的做法。

一些标准图书馆的例子:

另外,还有一个来自 Dave Cheney 的 邮寄,他建议用以下代码:

f, err := os.Open("testdata/somefixture.json")

我目前使用一个简洁的解决方案来解决这个问题,而不是通过调用 os.Open()直接打开文件,我使用嵌入包在一个聪明的方式:

首先,我在根包中创建一个全局变量,名为:

//go:embed config/* otherdirectories/*
var RootFS embed.FS

然后使用这个全局变量打开测试中的文件,例如:

func TestOpenConfig(t *testing.T) {
configFile, err := rootpkg.RootFS.ReadFile("config/env")
if err != nil {
t.Fatalf("unable to open config/env file: %s", err)
}


if string(configFile) != "FOO=bar\n" {
t.Fatalf("config file contents differ from expected: %s", string(configFile))
}
}

这是一个巧妙的技巧,因为现在您总是可以使用来自根包的相对路径,这是我过去在其他编程语言中所做的。

当然,这有导入根包所需的限制,由于循环导入,根包的布局可能并不理想,这取决于您的包布局。如果是这种情况,您可以在 config 目录本身内创建一个 embed.go文件,然后调用 您的配置名称。

另一个缺点是,您正在将测试文件嵌入到您的二进制文件中,如果您的测试文件不是非常大,比如兆字节,那么这可能没问题,所以我并不介意这个问题。

我还创建了一个库来说明这个解决方案:

Https://github.com/vingarcia/golang-reading-files-from-tests

Go 1.20为“ Go 子命令”获得了新的 -C参数,所以这应该有所帮助:

go test -C directory/ ...