You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go项目如何让相对路径同时兼容go run与go test运行场景?

问题原因

Go 执行go test命令时,每个包的测试进程的工作目录是该包自身的源码目录,而非项目根目录。你当前写死的相对路径是相对于项目根目录的,测试运行时自然找不到对应文件,同时你在init()函数中自动执行文件读取逻辑,会在包被导入时立刻触发读取,没有机会动态修改路径。


可行解决方案

方案1:使用go:embed内嵌密钥文件(推荐,无需处理路径问题)

Go 1.16及以上版本内置embed标准库,可以直接将静态资源编译到二进制文件中,完全不需要处理文件路径问题,同时适配go run、go test、Docker部署所有场景。
修改security/token/generator.go代码如下:

package security

import (
    "crypto/ecdsa"
    _ "embed"
    "log"

    "github.com/golang-jwt/jwt"
)

// 直接内嵌私钥文件,路径为当前go文件相对于私钥文件的相对路径
//go:embed ../../data/keys/512/private-key.pem
var privateKeyBytes []byte

var privateKey *ecdsa.PrivateKey

func init() {
    var err error
    privateKey, err = jwt.ParseECPrivateKeyFromPEM(privateKeyBytes)
    if  err != nil {
        log.Fatalf("Unable to parse ECDSA private key: %v", err)
    }
}

优点:完全不需要处理路径兼容问题,部署的时候只需要传二进制文件即可,不需要额外携带密钥文件。
注意:如果你的密钥会经常更换,不适合内嵌的话,可以选择下面的方案。


方案2:显式初始化+配置化路径

改掉init()自动读取的逻辑,改成显式调用的初始化函数,路径通过配置传入:

  1. 修改security/token/generator.go代码:
package security

import (
    "crypto/ecdsa"
    "io/ioutil"
    "log"

    "github.com/golang-jwt/jwt"
)

var privateKey *ecdsa.PrivateKey

// InitTokenGenerator 显式初始化,外部传入私钥路径
func InitTokenGenerator(privateKeyPath string) {
    data, err := ioutil.ReadFile(privateKeyPath)
    if err != nil {
        log.Fatalf("Can not read private key file: %v", err)
    }
    ecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(data)
    if  err != nil {
        log.Fatalf("Unable to parse ECDSA private key: %v", err)
    }
    privateKey = ecdsaKey
}
  1. 主程序main.go中,启动时调用初始化,传入相对项目根的路径:
func main() {
    security.InitTokenGenerator("data/keys/512/private-key.pem")
    // 其他启动逻辑
}
  1. 测试代码中,调用初始化时传入相对于测试包目录的路径,比如handler/auth_test.go中:
func init() {
    // 测试文件在handler目录下,相对路径往上走一层到项目根再找密钥
    security.InitTokenGenerator("../data/keys/512/private-key.pem")
}

优点:路径灵活可控,适合密钥需要外部配置、不打进二进制的场景,Docker部署时只需要把路径配置成容器内的密钥路径即可。


方案3:动态定位项目根目录

写一个工具函数,自动向上查找go.mod文件所在的目录作为项目根目录,自动拼接绝对路径:

// GetProjectRoot 自动获取项目根目录(go.mod所在目录)
func GetProjectRoot() (string, error) {
    dir, err := os.Getwd()
    if err != nil {
        return "", err
    }
    for {
        if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
            return dir, nil
        }
        parent := filepath.Dir(dir)
        if parent == dir {
            return "", fmt.Errorf("go.mod not found")
        }
        dir = parent
    }
}

然后读取密钥的时候拼接路径:

root, _ := GetProjectRoot()
privateKeyPath = filepath.Join(root, "data/keys/512/private-key.pem")

优点:不需要修改测试代码,自动适配所有运行场景。
缺点:部署的时候需要保证容器内运行目录下存在go.mod文件,或者需要额外判断生产环境的路径。


内容的提问来源于stack exchange,提问作者Muhammed Ozdogan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 03:15:05