0
myprogram/
 |
 |-main.go
 |-dir1/
     |-data/
         |-datafile.json
     |-runner.go
     |-runner_test.go

In runner.go, I have a simple function that reads the datafile.json. Something like

   func GetPayload() (string, err) {
            dBytes, dErr := ioutil.ReadFile("dir1/data/datafile.json")
            if dErr != nil { return nil, dErr}
            return dBytes, nil
   }

I'm using Go in a Lambda with a structure similar to above. When the Lambda runs in its actual environment, it starts at main.go, and then invokes GetPayload() from runner.go. However, I have a test in a simple worker node machine in runner_test.go that also hits GetPayload() .

During "normal" execution (from main.go) - this works OK. However, when GetPayload() is invoked from runner_test.go, it errors, saying

open dir1/data/datafile.json no such file or directory

This makes sense, because during the test, the working directory is the directory that houses runner_test.go, which is data/, so there is no dir1 as a child of it. I've been trying to play with using os.Getwd() and getting the paths from there like:

   pwd, _ := os.Getwd()
   dBytes, dErr := ioutil.ReadFile(pwd + "dir1/data/datafile.json")

But again, that won't work, because for runner_test.go pwd is user/myname/myprogram/dir1, but from main.go, it turns up as user/myname/myprogram.

Any idea how I can find and open datafile.json from within GetPayload() in any environment? I could pass an optional parameter to GetPayload() but if possible, it'd be great to avoid that.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
NateH06
  • 3,154
  • 7
  • 32
  • 56

3 Answers3

3

If the file is static (meaning that it doesn't need to change after you build the program), you can embed it into the built program. This means you no longer have to worry about run-time file paths.

import (
    "embed"
)

//go:embed data/*
var dataFiles embed.FS

func GetPayload() (string, err) {
    dBytes, dErr := dataFiles.ReadFile(dataFiles, "data/datafile.json")
    if dErr != nil { return nil, dErr}
    return dBytes, nil
}

Now the files in your data/ directory are embedded in this variable dataFiles which acts as a read-only file system.

For more info:

Read more about embed in the package documentation overview.

Read my answer about "when to use embed"

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
0

For data files that your program needs during runtime, either use a fixed directory and refer to that, or accept a command line argument or some sort of configuration that tells you where the file is.

When running unit tests, the wd is the directory containing the test file. One convention is to use a testdata/ directory under the directory containing the test, and put all data files there. That way you can refer to that file from the test by using testdata/datafile.json.

You can use a copy of the file you need during runtime as your test file, or you can use a symlink from the runtime data file to the test file under the testdata/ dir.

Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
0

For data files that your program needs during runtime, either use a fixed directory and refer to that

Someone made this suggestion, which I agree with. To that end, you can use something like this:

package main

import (
   "os"
   "path/filepath"
)

func main() {
   d, err := os.UserCacheDir()
   if err != nil {
      panic(err)
   }
   d = filepath.Join(d, "file.json")
   f, err := os.Open(d)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   os.Stdout.ReadFrom(f)
}
Zombo
  • 1
  • 62
  • 391
  • 407