-
Notifications
You must be signed in to change notification settings - Fork 428
Can't use godotenv when running tests #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I can't say I've seen that particular error before. I just tried to replicate by modifying some one of the tests for the package to trace the cwd on my laptop and I couldn't. I injected cwd, err := os.Getwd()
t.Log(err)
t.Log(cwd) to then run tests and get
What OS are you running (I just did this on my macbook)? I've got to admit I've no idea why your tests are running in tmp but happy to work with you to troubleshoot. |
Thanks for the response, I'm using a Macbook with OS X 10.9.5, and another colleague of mine has also run into this same issue when they tried using https://github.com/subosito/gotenv. Since you've confirmed that this is not expected behaviour, it must be something strange with the way our tests are being run, so I'll investigate further to figure out why this is happening on our end. Thanks again |
I've created a new empty project, and the tests are no longer run in a temporary dir, so that part has been sorted out, however, I was still unable to get godotenv to work. I've put the project here, and this is what I get when I run
So it seems that |
That one definitely is the expected behaviour, the dotenv file should be found at the execution root. If you're hoping to vary .env based on dev/test, you'll need to manually specify the file you want to load (relative to where you're executing). In the rails world (where the original library this duplicates comes from) you would have |
Thanks for the response @joho. I'm not actually trying to vary the It seems to be failing because during test mode, the tests are not looking for So it seems that there are (at least) two ways to solve this:
Have I understood this correctly? If this is indeed the situation, it might be worth making note of this in the readme file to explain that if you want to write tests for an application which is using |
Right. Apparently this is documented behaviour (see https://go-review.googlesource.com/c/go/+/17949) but I was never aware of it. I'll have a think of the best way to document (and/or a recommended work around) and get something into the readme. |
Here's the small snippet, to support different environments file dynamically. Based on the @adamcohen comment.(option 1) Create a config struct, to hold your environment values and to initiate the struct create a method similar to the one below.(In
In your go files, you can access the env values as below
When you run without setting any If you want to run the tests with values in Example:
|
Why not use the godotenv command like
An extra benefit is that you can specify a test .env file.
Or even better you can just overwrite some env variables by using multiple files
|
Relative path args also work for
|
Thanks @DanielOchoa I didn't know relative path args existed! |
This was failing for me too. @adamcohen 's first solution worked. Thanks. I would suggest to add some info about this behaviour in the readme, or at least link to this issue. |
This problem gets even more tricky with Go Modules as your code can (and IMO should) live outside of My FWIW I run my tests like: testutils.go: package testutils
import (
"os"
"regexp"
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
)
const projectDirName = "my-cool-project"
// LoadEnv loads env vars from .env
func LoadEnv() {
re := regexp.MustCompile(`^(.*` + projectDirName + `)`)
cwd, _ := os.Getwd()
rootPath := re.Find([]byte(cwd))
err := godotenv.Load(string(rootPath) + `/.env`)
if err != nil {
log.WithFields(log.Fields{
"cause": err,
"cwd": cwd,
}).Fatal("Problem loading .env file")
os.Exit(-1)
}
} I just do a simple call from my test setup call: func TestMain(m *testing.M) {
testutils.LoadEnv()
...
os.Exit(m.Run())
} Hope this helps someone else |
thanks to @rynop I do something like this :
|
Thanks, guys I am new to Go, and this helped me a lot. |
Is there a known problem with godotenv.Load() in different namespaces ? somehow my main.go file can find the .env file (placed in root), but the base.go file(namespace models) can't find it, im sure im doing something wrong but i can't point out what exactly. So for better understanding: -root
|
Or is it possible that i have a problem because I user godotenv.Load() in the main.go and in the base.go ? |
This solution on SO https://stackoverflow.com/a/38644571 worked for me and seems quite straightforward
It gives you the directory of the current file, and from there you can navigate to the .env file you want.
and in the end my version of their solution looked like this
|
Does not work with both test and running the application |
This is my workaround, it searches upward recursively from the working directory until it finds the file import (
"fmt"
"os"
"path"
"github.com/joho/godotenv"
)
func searchup(dir string, filename string) string {
if dir == "/" || dir == "" {
return ""
}
if _, err := os.Stat(path.Join(dir, filename)); err == nil {
return path.Join(dir, filename)
}
return searchup(path.Dir(dir), filename)
}
func SetEnviroment(env string) error {
directory, err := os.Getwd()
if err != nil {
return err
}
filename := fmt.Sprintf(".env.%s", env)
filepath := searchup(directory, filename)
if filepath == "" {
return fmt.Errorf("could not find dot env file: %s", filename)
}
return godotenv.Load(filepath)
} somewhere in my test suite require.NoError(t, utils.SetEnviroment("test")) // load .env.test somewhere in my app if os.Getenv("ENV") {
utils.SetEnviroment(os.Getenv("ENV")) // loads .env.{ENV}
} |
I ended up with using an env for it... I found providing config path explicitly is better, of course there should be default value.
|
I hope this function helps you
Or if you have two .env and .env.test files
|
use environment variable to specify .env file direcotry. package configs
func LoadEnv() {
envRoot := os.Getenv("ENV_ROOT")
if "" == envRoot {
envRoot = "./"
}
env := os.Getenv("ENV")
if "" == env {
env = "dev"
}
err := godotenv.Load(envRoot + ".env." + env + ".local")
log.Print(err)
if "test" != env {
err = godotenv.Load(envRoot + ".env.local")
log.Print(err)
}
err = godotenv.Load(envRoot + ".env." + env)
log.Print(err)
err = godotenv.Load(envRoot + ".env") // The Original .env
log.Print(err)
} |
I'm using it like below: const defaultEnvPath = "/envs/.env"
func LoadEnv() {
_, f, _, ok := runtime.Caller(0)
if !ok {
log.Fatal("Error generating env dir")
}
dir := filepath.Join(filepath.Dir(f), "../..", defaultEnvPath)
err := godotenv.Load(dir)
if err != nil {
log.Fatal("Error loading .env file")
}
} |
I place my env files at the same dir as my go.mod. As every Go project must have a go.mod, I could implement like this. The // Load loads the environment variables from the .env file.
func Load(envFile string) {
err := godotenv.Load(dir(envFile))
if err != nil {
panic(fmt.Errorf("Error loading .env file: %w", err))
}
}
// dir returns the absolute path of the given environment file (envFile) in the Go module's
// root directory. It searches for the 'go.mod' file from the current working directory upwards
// and appends the envFile to the directory containing 'go.mod'.
// It panics if it fails to find the 'go.mod' file.
func dir(envFile string) string {
currentDir, err := os.Getwd()
if err != nil {
panic(err)
}
for {
goModPath := filepath.Join(currentDir, "go.mod")
if _, err := os.Stat(goModPath); err == nil {
break
}
parent := filepath.Dir(currentDir)
if parent == currentDir {
panic(fmt.Errorf("go.mod not found"))
}
currentDir = parent
}
return filepath.Join(currentDir, envFile)
} |
This is terrible! I just spent hours and hours trying to work around this in several ways. As @joho mentioned it's a documented behaviour in I think the cleaner way to do it at the moment is to use the CLI I would like to add that I am using following convention for
So, whenever FROM golang:1.20.4
ENV GOMOD=/api/go.mod
WORKDIR /api
COPY . .
RUN go install github.com/joho/godotenv/cmd/godotenv@latest
RUN go mod tidy
RUN ENVIRONMENT=test godotenv -f "${ENVIRONMENT}.env" go test -v ./...
CMD godotenv -f ${ENVIRONMENT}.env go run main.go My conclusion is that this is not an error from |
Ao executarmos todos os testes do projeto, o método init() do package config é iniciado, porém o arquivo .env não é encontrado já que o comando `go test` não é executado na raíz do projeto. Isso é um problema descrito em joho/godotenv#43.
Copy your .env file which is in root directory, and Paste it in your test directory. It worked for me. |
My guess is that when running |
Iam new into golang, this fixed my issue. Thanks |
I end up loading lastCwd := ""
for {
cwd, err := os.Getwd()
if err != nil {
break
}
if cwd == lastCwd {
// reached root dir
break
}
err = godotenv.Load()
if err == nil {
// success
break
}
if os.Chdir("..") != nil {
break
}
lastCwd = cwd
} |
use absolute path like this envPath := "your directory/.env" // setting with your absolute path
if err := godotenv.Load(envPath); err != nil {
log.Fatalf("Error loading .env file: %v", err)
} |
Ha! It's 2024 and this is still a pain :D. Using MacOS M1 here. Tried many suggestions, as well as from my old source codes. The readme led me to the solution, at least in my case.
|
Here is my solution, if you don't want to hardcode your env path and also you have your func LoadENV() {
path := ".env"
for {
err := godotenv.Load(path)
if err == nil {
break
}
path = "../" + path
}
} |
This is my current solution, have your .env file at the root of your directory: var configPath string
// Find the current file path
_, filename, _, _ := runtime.Caller(0)
currentFileDir := filepath.Dir(filename)
// Walk up the directory tree to find the .env file
for {
configPath = filepath.Join(currentFileDir, ".env")
if _, err := os.Stat(configPath); !os.IsNotExist(err) {
break
}
parentDir := filepath.Dir(currentFileDir)
if parentDir == currentFileDir {
return nil, fmt.Errorf(".env file not found")
}
currentFileDir = parentDir
}
err := godotenv.Load(configPath) |
This worked for me. It is terrible that this language cannot handle it naturally... |
I've added
godotenv
to my project and it works fine when running the compiled binary, but it fails when running my tests:This is happening because the tests are being run in the following temp dir location:
and it can't find the
.env
file. Can anyone tell me how to fix this issue? Do I need to specify the absolute path to the.env
file? I'm using the following code to load the.env
file without specifying a path:The text was updated successfully, but these errors were encountered: