9
votes

Hi I am new to Go and I am writing a simple app which gets some configuration from the env variables. I do this in the init function as shown below.

type envVars struct {
    Host     string `env:"APP_HOST"`
    Username string `env:"APP_USERNAME"`
    Password string `env:"APP_PASSWORD"`
}

var envConfig envVars

func init() {
    if err := env.Parse(&envConfig); err != nil {
        log.Fatal(err)
    }
}

I wrote test to verify of the env variables are being read correctly. But the problem is that my program's init func gets called even before my test's init func. Is there any way I can do some sort of setup before my program's init func gets called.

func init() {
    os.Setenv("APP_HOST", "http://localhost:9999")
    os.Setenv("APP_USERNAME", "john")
    os.Setenv("APP_PASSWORD", "doe")
}

func TestEnvConfig(t *testing.T) {
    assert.NotNil(t, envConfig)
    assert.Equal(t, "http://localhost:9999", envConfig.Host)
}
4
Not really. The point of the init function is to be called automatically before all other functions. If you want to be able to do something before init, you need to move it out of init. (also reading env variables isn't something you would normally test. The code that reads them should be tested separately) - JimB

4 Answers

4
votes

You can use the TestMain func to control what happens before and after your tests.

For example:

func TestMain(m *testing.M) {
    // Write code here to run before tests
    
    // Run tests
    exitVal := m.Run()
    
    // Write code here to run after tests

    // Exit with exit value from tests
    os.Exit(exitVal)
}

func TestYourFunc(t *testing.T) {
    // Test code
}
0
votes

Less than ideal, but this works for me. Inside of the package that you're testing:

func init() {
    if len(os.Args) > 1 && os.Args[1][:5] == "-test" {
        log.Println("testing")//special test setup goes goes here
        return // ...or just skip the setup entirely
    }
    //...
}
0
votes

You can add a Test_parse_params(t *testing.T) function before your real tests. Look like this:

type envVars struct {
    Host     string `env:"APP_HOST"`
    Username string `env:"APP_USERNAME"`
    Password string `env:"APP_PASSWORD"`
}

var envConfig envVars

//parse command params
func Test_parse_params(t *testing.T) {
    if err := env.Parse(&envConfig); err != nil {
        log.Fatal(err)
    }
}

func Test_real_test(t *testing.T) {
....
}
0
votes

No, you shouldn't expect init() run in some order, (in fact it based on file loaded order, but still, you should not count on it).

The simple way is, if you want to test it, use a shell script to run you test, or something like Makefile.

Shell example:

set +e
export APP_HOST=http://localhost:9999
export APP_USERNAME=john
export APP_PASSWORD=doe
go test .
unset APP_HOST
unset APP_USERNAME
unset APP_PASSWORD

or a single line command:

APP_HOST=http://localhost:9999 APP_USERNAME=john APP_PASSWORD=doe go test .

Edit:

Other solution: move out the read env from init func.

func init(){
  envInit()
}
func envInit(){
 if err := env.Parse(&envConfig); err != nil {
        log.Fatal(err)
    }
}

Then you can call again envInit in your test to make sure it works.