Golang:测试和工作目录

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

我正在用 Go 为我的应用程序编写一些单元测试。然而测试失败,因为它找不到配置文件。通常,二进制文件会在路径

conf/*.conf
下的工作目录中查找配置文件。

我认为浏览到具有

conf/
的目录并在其中运行
go test
可以解决该问题,但它仍然报告文件系统找不到指定的路径。

如何告诉

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

unit-testing go working-directory
13个回答
56
投票

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

package sample

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

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

30
投票

我不相信这是可能的。我无法找到明确说明这一点的文档,但我相信

go test
始终使用包目录(包含 go 源文件)作为工作目录。


20
投票

作为解决方法,我编译了测试并从当前目录执行测试。

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

或者,如果您想要可以使用的通用命令,您可以使用

-o
选项重命名测试文件。

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


15
投票

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

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

您的路径应始终从您的项目目录开始。每次您读取包中的文件并由 main.go 或另一个包单元测试访问时。它将永远有效。


12
投票

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

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"

11
投票

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

10
投票

您可以使用 os 软件包

你会想做这样的事情

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

        //TEST FUNCTION

        os.Chdir("..")
    }

os 包中有多种可能性。


2
投票

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

由于没有获取项目目录的本机方法,您可以识别一些您知道它仅位于项目根目录中的文件或目录(在我的例子中,它是相对目录database/migrations)。一旦有了这个唯一的相对目录,您就可以使用如下函数来获取项目根目录。它只是获取当前工作目录(假设它位于项目目录内)并开始一直向上导航,直到找到一个目录,该目录具有您知道它位于项目根目录上的相对目录:

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

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


2
投票

Go 1.20 正在为“go 子命令”添加新的

-C
参数,因此这应该有所帮助:

go test -C directory/ ...

2
投票

我目前使用一个巧妙的解决方案来解决这个问题,而不是通过调用直接打开文件

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


1
投票

我遇到了类似的问题并找到了解决方案在此博客上

基本上,您可以使用类似的功能更改测试运行的文件夹:

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

0
投票

Go 中的常见做法是将测试装置放在同一个包中内的

testdata
文件夹中。

标准库中的一些示例:

此外,还有 Dave Cheney 的 post,他建议使用以下代码:

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

-1
投票

我将使用环境变量作为您的应用程序的位置。这似乎是运行 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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.