tests, init() and flags

1,972 views
Skip to first unread message

Shivendra Singh

unread,
Apr 24, 2021, 4:37:28 PM4/24/21
to golang-nuts
Hi!!

I have a use case as following :
1. There is a go program that is used for managing run time configs of our applications. (basically all our run time configs are in GitHub and this code reads and provides to app based on certain run time args as mentioned below) This program is added as go mod dependency and exposes certain functions like GetString(key).
2. This program is a client program and does not contain any main() however makes use of init() method on client app startup. Therefore whenever i start my application, this code runs it's init() method, now in the init() method it parses flag to read variables such as env and app name that is required to load the appropriate config.

The Problem :
All works fine, in the actual code, however whenever i try to run the unit test cases i get error message like : 

flag provided but not defined: -test.paniconexit0
  -appName string
        app name
  -envName string
        environment name

I tried to provide args when running test like :
go test -appName  appName -envName dev

However the approach seems not to be working. I have read at a few places about using something like :
var _ = func() bool {
testing.Init()
return true
}()
But it did not helped me either in this case.
I am only 1 months old in go so any help will be greatly appreciated.

Thank You in Advance!!

Jan Mercl

unread,
Apr 24, 2021, 4:57:15 PM4/24/21
to Shivendra Singh, golang-nuts
On Sat, Apr 24, 2021 at 10:37 PM Shivendra Singh
<shivendra...@gmail.com> wrote:

> ..., now in the init() method it parses flag to read variables such as env and app name that is required to load the appropriate config.

IINM, calling flag.Parse anywhere else than in main() or TestMain() is
not really compatible with what go test relies on.

flag.Parse changes shared state, doing it without coordination from
different places in order that might be not under the control of the
package that does it in its init() may create a mess that would be
consistent with your observation.

Shivendra Singh

unread,
Apr 24, 2021, 5:10:08 PM4/24/21
to golang-nuts
Yes @Jan Mercl  absolutely correct!!

Able to figure out a solution.
Basically the go mod i am using as part of the code is doing something like this :
func init() {
//reads vars
flag.Parse()
}

The problem arises when doing flag.Parse() as part of the init.
https://github.com/golang/go/issues/31859#issuecomment-489889428 gives very valuable insights to the issue.

High Level Problems that may occur :
1. if i am reading any command line args anywhere else it will fail, since in this code flag.Parse() is happening during init()
2. The code snippet :
var _ = func() bool {
testing.Init()
return true
}()
This somewhat of a hack to explicitly invoke init() method and only helps in a very few cases.

As a solution, i went ahead to remove the flag.Parse() from init() because it is really wrong and rather call it from my main().
Just doing this change unblocked me for testing as now from test method i can easily invoke a setup call to do flag.Parse() as Jan Mercl pointed out.

Amit Saha

unread,
Apr 24, 2021, 10:43:35 PM4/24/21
to Shivendra Singh, golang-nuts
On Sun, Apr 25, 2021 at 7:10 AM Shivendra Singh
<shivendra...@gmail.com> wrote:
>
> Yes @Jan Mercl absolutely correct!!
>
> Able to figure out a solution.
> Basically the go mod i am using as part of the code is doing something like this :
> func init() {
> //reads vars
> flag.Parse()
> }
>
> The problem arises when doing flag.Parse() as part of the init.
> https://github.com/golang/go/issues/31859#issuecomment-489889428 gives very valuable insights to the issue.
>
> High Level Problems that may occur :
> 1. if i am reading any command line args anywhere else it will fail, since in this code flag.Parse() is happening during init()
> 2. The code snippet :
> var _ = func() bool {
> testing.Init()
> return true
> }()
> This somewhat of a hack to explicitly invoke init() method and only helps in a very few cases.
>
> As a solution, i went ahead to remove the flag.Parse() from init() because it is really wrong and rather call it from my main().
> Just doing this change unblocked me for testing as now from test method i can easily invoke a setup call to do flag.Parse() as Jan Mercl pointed out.

I would also recommend that you create a new FlagSet object
(https://golang.org/pkg/flag/#NewFlagSet) and use that to write more
testable applications. So, for example:

func setupFlagsAndParse(args []string) {
fs := flag.NewFlagSet(..)
# setup the flag and options as usual
fs.Parse(args)
...
}

Then, you call this function from main() as
setupFlagsAndParse(os.Args[1:]) or your tests with any slice of
strings that you may want to test.

Abdennour Elm

unread,
Nov 23, 2021, 12:48:28 AM11/23/21
to golang-nuts
@amit this was my best way : NewFlagSet. This is full example including testing: https://eli.thegreenplace.net/2020/testing-flag-parsing-in-go-programs/

Best

Reply all
Reply to author
Forward
0 new messages