golang test temp directory

18,852

Solution 1

Just to compare vs. what you have with ioutil.TempDir, here's what things look like with io.Reader:

// Load main.conf from the specified file path
func LoadMainSettings(src io.Reader) (*MainSettings, error) {
    b, err := ioutil.ReadAll(src)
    if err != nil { return nil, err }

    r := &MainSettings{}
    err = json.Unmarshal(b, r)
    if err != nil { return nil, err }

    return r, nil
}

Specifically, we change the argument from a path string to a src io.Reader instance, and we replace the ioutil.ReadFile with an ioutil.ReadAll.

The test case that you've written then ends up being a bit shorter precisely because we can dispense with file operations:

s, err := LoadMainSettings(strings.NewReader("{...sample config data...}"))

Solution 2

Since Go version 1.15 there is now T.TempDir() in the standard testing package. The docs explain it as follows:

TempDir returns a temporary directory for the test to use. The directory is automatically removed by Cleanup when the test and all its subtests complete. Each subsequent call to t.TempDir returns a unique directory; if the directory creation fails, TempDir terminates the test by calling Fatal.

Solution 3

You could use ioutil.TempDir or TempFile from the same package.

Share:
18,852

Related videos on Youtube

Brad Peabody
Author by

Brad Peabody

Me? I code stuff.

Updated on September 14, 2022

Comments

  • Brad Peabody
    Brad Peabody over 1 year

    I have a simple function which parses a config file as JSON. I want to write a test which either uses some sample static config files and parses them, or creates the samples during the test and tries to parse them.

    It's not entirely necessary to the question, but here is the basic code:

    // config.go
    
    // ...(package,imports)...
    
    // Overall settings - corresponds to main.conf
    type MainSettings struct {
        // stuff
    }
    
    // Load main.conf from the specified file path
    func LoadMainSettings(path string) (*MainSettings, error) {
    
        b, err := ioutil.ReadFile(path)
        if err != nil { return nil, err }
    
        r := &MainSettings{}
        err = json.Unmarshal(b, r)
        if err != nil { return nil, err }
    
        return r, nil
    
    }
    

    and the test:

    // config_test.go
    
    func TestLoadMainSettings(t *testing.T) {
    
        // possibly generate some example config files,
        // or use static samples packaged with the source
    
        s, err := LoadMainSettings("conf/main.conf") // <-- what should this path be??
        if err != nil { panic(err) }
    
        // more sanity checking...
    
    }
    

    That said, my specific questions are:

    • Is there a proper place for static assets (like sample config files) that are only applicable to tests?
    • During test execution is there a proper (cross platform, gets cleaned up with 'go clean') location to write out temporary files?

    (Note: I run most of my stuff on Linux for staging and production and Mac for local dev - so using /tmp/ as a temp dir for tests works for me in practice. But was wondering if there's a better way...)


    EDIT: Ended up using this approach for the test:

    f, err := ioutil.TempFile("", "testmainconf")
    if err != nil { panic(err) }
    defer syscall.Unlink(f.Name())
    ioutil.WriteFile(f.Name(), []byte("{...sample config data...}"), 0644)
    
    s, err := LoadMainSettings(f.Name())
    

    But the other suggestion of making LoadMainSettings accept an io.Reader instead of a string is also a good idea.

    • dyoo
      dyoo over 10 years
      Can you write your test so it uses an io.Reader directly? If so, then your test case won't need to depend on the file system, as your tests can use strings.NewReader to provide the appropriate test content in the test itself.
    • Tyler
      Tyler over 10 years
      It shouldn't be more cumbersome for the caller. A file is an io.Reader already.