在Golang测试中,是否可以将当前包结构的根目录作为字符串获取?

huangapple go评论78阅读模式
英文:

Is it possible to get the current root of package structure as a string in golang test?

问题

我正在为我的单元测试编写一个实用函数,该函数被多个包中的单元测试使用。这个实用函数必须读取一个特定的文件(始终是同一个文件)。以下是三种不起作用的解决方案,用于解释我正在寻找的内容以及原因。

  1. 硬编码绝对路径。这种方法失败的原因是试图测试项目的另一个用户可能在绝对路径上有不同的前缀。

  2. 从定义实用函数的文件的路径中硬编码相对路径。这种方法失败的原因是导入和使用此函数的包不一定与定义实用函数的文件在文件层次结构中处于相同的级别,而相对路径是相对于导入者而不是被导入者解释的。

  3. 从每个调用者的包相对于调用者的相对路径传入文件的相对路径。这种方法实际上是可行的,但似乎非常冗长,因为现在每个调用者都必须更改以传递一个文件。

我看到了第四种解决方案,可以在实用函数中硬编码一个相对于顶级包的根目录的路径。然而,我尚未找到一种方法来在代码中获取根目录,尽管我怀疑有一种方法,因为可以从根目录解析导入。

因此,我如何获取所需的根目录?

我已经查阅了各种Go文档,但迄今为止未能找到解决方案。我还看到了这个问题,但那里的解决方案与上述的第3种方法相同。

英文:

I am writing a utility function for my unit tests which is used by unit tests in multiple packages. This utility function must read a particular file (always the same file). Here are three solutions which do not work, to explain what I am looking for and why.

  1. Hardcode the absolute path. This fails because another user who is trying to test the project might have a different prefix on the absolute path.

  2. Hardcode a relative path from the path of the file which defines the utility function. This fails because packages which import and use this function are not necessarily at the same level of the file hierarchy as the file that defines the utility function, and relative paths are interpreted relative to the importer, not the imported.

  3. Pass in the relative path to the file from every caller relative to the caller's package. This actually works but seems to be very verbose because now every caller must be changed to pass one file.

I see a fourth solution, whereby I can hardcode a path in the utility function which is relative to the root directory of the top-level package. However, I have not been able to find a way to get the root directory in code, although I suspect there is one because imports can be resolved from the root.

Thus, how might I get the sought-after root directory?

I've looked over various Go documents but have so far failed to find a solution. I have also seen this question but the solution there is equivalent to #3 above.

答案1

得分: 90

你也可以不使用 C 语言来使用我的方法:

package mypackage

import (
    "path/filepath"
    "runtime"
    "fmt"
)

var (
    _, b, _, _ = runtime.Caller(0)
    basepath   = filepath.Dir(b)
)

func PrintMyPath() {
    fmt.Println(basepath)
}

链接:https://play.golang.org/p/ifVRIq7Tx0

英文:

You can also use my method without C:

package mypackage

import (
    "path/filepath"
    "runtime"
    "fmt"
)

var (
    _, b, _, _ = runtime.Caller(0)
    basepath   = filepath.Dir(b)
)

func PrintMyPath() {
    fmt.Println(basepath)
}

https://play.golang.org/p/ifVRIq7Tx0

答案2

得分: 39

根据Oleksiy的回答,你可以在项目中创建一个名为./internal/projectpath/projectpath.go的子包,并粘贴以下代码:

package projectpath

import (
	"path/filepath"
	"runtime"
)

var (
	_, b, _, _ = runtime.Caller(0)

	// 该项目的根文件夹
	Root = filepath.Join(filepath.Dir(b), "../..")
)

然后你可以在任何其他包中使用projectpath.Root来获取你的项目的根文件夹。

英文:

Building on the answer by Oleksiy you can create a sub-package in your project called ./internal/projectpath/projectpath.go and paste in the following:

package projectpath

import (
	"path/filepath"
	"runtime"
)

var (
	_, b, _, _ = runtime.Caller(0)

	// Root folder of this project
	Root = filepath.Join(filepath.Dir(b), "../..")
)

Then you can use projectpath.Root in any other package to have the root folder of your project.

答案3

得分: 23

返回应用程序的根目录:

import (
    "path"
    "path/filepath"
    "runtime"
)  

func RootDir() string {
    _, b, _, _ := runtime.Caller(0)
    d := path.Join(path.Dir(b))
    return filepath.Dir(d)
}
英文:

Returns the root of the application:

import (
	"path"
	"path/filepath"
	"runtime"
)  

func RootDir() string {
	_, b, _, _ := runtime.Caller(0)
	d := path.Join(path.Dir(b))
	return filepath.Dir(d)
}

答案4

得分: 14

Go目录:

// 从可执行目录获取
ex, _ := os.Executable()
fmt.Println("可执行目录:", filepath.Dir(ex))

// 当前工作目录
dir, _ := os.Getwd()
fmt.Println("当前工作目录:", dir)

// 运行时目录的相对路径
_, b, _, _ := runtime.Caller(0)
d1 := path.Join(path.Dir(b))
fmt.Println("相对路径:", d1)

请注意,这是Go语言的代码片段,用于获取不同目录的路径信息。

英文:

Go directories:

// from Executable Directory
ex, _ := os.Executable()
fmt.Println("Executable DIR:", filepath.Dir(ex))

// Current working directory
dir, _ := os.Getwd()
fmt.Println("CWD:", dir)

// Relative on runtime DIR:
_, b, _, _ := runtime.Caller(0)
d1 := path.Join(path.Dir(b))
fmt.Println("Relative", d1)

答案5

得分: 1

这是一个测试助手的示例,用于获取模块(项目)的根路径,而无需将其设置为全局变量,或者使用相对路径(如..),而是利用go list来生成路径(如果需要在运行时生成路径)或者不想受限于变量定义在哪个文件中。

func getRelativeRootPath(tb testing.TB) string {
    tb.Helper()

    importPath := runGoList(tb, "list", "-f", "{{.ImportPath}}")
    modulePath := runGoList(tb, "list", "-m", "-f", "{{.Path}}")
    pkgPath := runGoList(tb, "list", "-f", "{{.Dir}}")

    relativePath, err := filepath.Rel(importPath, modulePath)
    if err != nil {
        tb.Fatal(err)
    }
    return filepath.Join(pkgPath, relativePath)
}

func runGoList(tb testing.TB, arg ...string) string {
    tb.Helper()
    cmd := exec.Command("go", arg...)
    output, err := cmd.CombinedOutput()
    if err != nil {
        tb.Fatalf("runGoList: %v: %s", err, string(output))
    }
    return strings.TrimSpace(string(output))
}

这将独立于在使用该助手的测试所在的包中运行go test,或者在根级别运行go test


导入路径是引用包的路径,模块路径仅包含模块本身的路径,我们在这里使用它作为参考来确定另一个包嵌套的深度,从而允许我们计算相对路径,例如...../..等等。

最后,我们还使用runtime.Caller(0)来获取当前文件的路径,但现在可以在运行时计算项目的根路径。

英文:

Here is an example of a test helper to get the root path of your module (project) without having to set it as global variable, or using relative paths like .. by utilize go list in case you need to generate the path at runtime or if you don't want to be bound by in which file the variable is defined.

func getRelativeRootPath(tb testing.TB) string {
	tb.Helper()

	importPath := runGoList(tb, "list", "-f", "{{.ImportPath}}")
	modulePath := runGoList(tb, "list", "-m", "-f", "{{.Path}}")
	pkgPath := runGoList(tb, "list", "-f", "{{.Dir}}")

	relativePath, err := filepath.Rel(importPath, modulePath)
	if err != nil {
		tb.Fatal(err)
	}
	return filepath.Join(pkgPath, relativePath)
}

func runGoList(tb testing.TB, arg ...string) string {
	tb.Helper()
	cmd := exec.Command("go", arg...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		tb.Fatalf("runGoList: %v: %s", err, string(output))
	}
	return strings.TrimSpace(string(output))
}

This will work independent on running go test in the package where the test makes use of the helper or if go test is run at the root level.


The import path is the path of the referenced package, the module path contains only the path for the module itself which we use here as a reference to figure out how deep another package is nested, allowing us to compute a relative path, for example . or .. or ../.., and so on.

Finally we also use runtime.Caller(0) to get the path of the current file but can now at runtime compute the root path of the project.

答案6

得分: -3

是的,可以找到包路径:

pathfind.go:

package main

/*
const char* GetMyPathFILE = __FILE__;
*/
import "C"
import "path/filepath"

var basepath = ""

// GetMyPath 返回此文件(pathfind.go)的绝对目录
func GetMyPath() string {
    if basepath == "" {
        g := C.GoString(C.GetMyPathFILE)
        basepath = filepath.Dir(g)
    }
    return basepath
}

你只需要将这个文件复制到你的项目中。请记住,这会提供文件的路径,而不是调用者的路径,所以你需要将这个函数/文件复制到每个需要该函数的项目中。另外,如果你将这个代码放在其他代码文件中,请确保遵守 CGO 的导入规则。

英文:

Yes, Finding package path is possible:

pathfind.go:

package main

/*
const char* GetMyPathFILE = __FILE__;
*/
import "C"
import "path/filepath"

var basepath = ""

//GetMyPath Returns the absolute directory of this(pathfind.go) file
func GetMyPath() string {
	if basepath == "" {
		g := C.GoString(C.GetMyPathFILE)
		basepath = filepath.Dir(g)
	}
	return basepath
}

All you have to do is copy this file to your project. Keep in mind this comes up with the path for the file, not the caller so you have to copy the function/file to every project you need the function in. Additionally if you put this in a file with other code be sure to respect CGO's import rules.

huangapple
  • 本文由 发表于 2015年8月7日 16:46:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/31873396.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定