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")
}