Home > Mobile >  unittest - setting env variables before init method of file in package
unittest - setting env variables before init method of file in package

Time:10-07

I have the following layout in my project :

MyProject
├── config
│   └── config.go
│   └── config_test.go
│   └── testdata
│         └── config.yaml
├── main.go
├── go.mod
├── go.sum  

The config.go file responsible for loading the app's configuration and it should happen automatically on the first time the config package is imported. Therefore, I configured there an init method that will load all the relevant settings and set it :

package config
import ( "os" )
    var (
        Propery_x string
        Property_y string
        Property_z string
    )
    func init() {
        LoadSettings()
    }
    
    func LoadSettings(){
        Property_x = os.Getenv("X")
        Property_y = os.Getenv("Y")
        Property_z = os.Getenv("Z")
        
       .....

I'm having a problem with setting those enviroment values in the unit tests before the init method of config.go file is called. I followed some guidelines I found here that recommend doing the following :

package config
import (
    "os"
    "testing"

    "github.com/google/go-cmp/cmp"
    "github.com/stretchr/testify/assert"
)
    func TestMain(m *testing.M) {
        os.Setenv("X", "500")
        m.Run()
        os.Exit(0)
    }
    
    func TestLoadSettings(t *testing.T) {
        assert.Equal(t, "500", Property_x, "values arent equal..")
    }

As you can understand, the code still uses the default value and the env variable isnt set before the init() of config.go.

Another option that I considered : Lazy Loading - in this way I wont need the init method and the settings will be loaded in the first time someone tries to access the configuration.

CodePudding user response:

Don't use init functions.

Instead call config.LoadSettings on startup in your main function. In the tests you can then also define the exact sequence of things.


There are also packages that can load configuration files, environment variables or command line parameters using the exact same syntax the flags package uses. e.g. namsral/flags

CodePudding user response:

Like @TehSphinX suggested, I decided to use a dedicate method that will return the configuration and not load it automatically. In other words, I implemented lazy loading :

 package settings
    var (
        settings       *Settings
        configLoaded *AtomicBoolean = &AtomicBoolean{sync.Mutex{}, false}
    )
    
    type AtomicBoolean struct {
        mu  sync.Mutex
        val bool
    }
    
    type Settings struct {
        property_x string
        property_y string
        property_z string
    }
    
    func (ab *AtomicBoolean) getValue() bool {
        var val bool = false
        ab.mu.Lock()
        val = ab.val
        ab.mu.Unlock()
        return val
    }
    func (ab *AtomicBoolean) setValue(val bool) {
        ab.mu.Lock()
        ab.val = val
        ab.mu.Unlock()
    }
    
    func GetSettings() *Settings {
       if !settingsgLoaded.getValue(){
           loadSettings()
       }
    
    }
    func loadSettings(){
            Property_x = os.Getenv("X")
            Property_y = os.Getenv("Y")
            Property_z = os.Getenv("Z")
            ...
            settingsLoaded.setValue(true)
            .....

Now in my tests I can set the env variables and only after call the GetSettings func :

package settings
    func TestLoadSettings(t testing.T){
       os.Setenv("X',"500")
       assert.Equal(GetSettings().property_x,"500")
    }
  •  Tags:  
  • go
  • Related