17

Have a moduled application. Have a bunch of tests that use a set of application modules, each test requires different set. Some modules are tuned through the command-line, e.g:

func init() {
    flag.StringVar(&this.customPath, "gamedir.custom", "", "Custom game resources directory")
}

But I cannot test this functionality. If I run

go test -test.v ./... -gamedir.custom=c:/resources

the runtime answers with

flag provided but not defined: -gamedir.custom

and fails the test.

What am I doing wrong with testing command-line args?

Kevin Burke
  • 61,194
  • 76
  • 188
  • 305
snuk182
  • 1,022
  • 1
  • 12
  • 28
  • Did you try `go test -test.v -gamedir.custom=c:/resources ./...`? – VonC Dec 07 '14 at 13:28
  • Sure. Go test says the following: can't load package: package .: no buildable Go source files in c:\goworkspace-win\wnd\src Besides, this also noted in documentation (http://golang.org/src/cmd/go/test.go ): UsageLine: "test [-c] [-i] [build and test flags] [packages] [flags for test binary]" – snuk182 Dec 07 '14 at 14:08
  • What's weird is that the documentation for go test says what you're doing should be fine. you might not be able to access your flags like that, but it should not output this error message. – Not_a_Golfer Dec 07 '14 at 14:10
  • Well. Seen somewhere that the best practice for usage of command line arguments is to read all them in one place with "flags". Unfortunately this is loosely suitable for my case, because each module may have (or have not) the flags of its own, and arranging all possible flags in one place will result in a mess and huge refactoring, which is possible, of course, but not good for architecture. – snuk182 Dec 07 '14 at 14:16
  • @snuk182 I'm using a package called go-flags that is more advanced and supports configuration files on top of arguments. Then what I do is simply pass the config file path to it via ENV vars, avoiding this issue. I strongly recommend it, regardless of this specific problem. – Not_a_Golfer Dec 07 '14 at 15:14
  • @snuk182 link: https://github.com/jessevdk/go-flags – Not_a_Golfer Dec 07 '14 at 15:14

4 Answers4

19

I think I got it what is wrong with flags in my case. With the following command

go test -test.v ./... -gamedir.custom=c:/resources

the compiler runs one or several tests on a workspace. In my particular case there are several tests, because ./... means find and create test executable for every _test.go file found. The test executable applies all the additional params unless one or some of them is ignored within it. Thus the test executables that do use param pass the test, all others fail. This may be overridden by running go test for each test.go separately, with appropriate set of params respectively.

snuk182
  • 1,022
  • 1
  • 12
  • 28
  • I recently asked a similar question here: https://stackoverflow.com/questions/49927650/flag-in-test-causes-other-tests-to-fail-to-run I decided to use env vars instead so I could run my whole suite – DMac the Destroyer Apr 30 '18 at 15:14
13

You'll also get this message if you put your flag declarations inside of a test. Don't do this:

func TestThirdParty(t *testing.T) {
    foo := flag.String("foo", "", "the foobar bang")
    flag.Parse()
}

Instead use the init function:

var foo string
func init() {
    flag.StringVar(&foo, "foo", "", "the foo bar bang")
    flag.Parse()
}

func TestFoo() {
    // use foo as you see fit...
}
Kevin Burke
  • 61,194
  • 76
  • 188
  • 305
  • Thanks. This is preferrable solution for general purposes and I use it widely. However in this particular case I have a test which tests filling a bunch of modules (of unknown length and unknown but configurable types) with values picked by keys got from each module. Every flag key is unique, but neither count of them nor names are known at the start of test. Yes, I admit that this practice is considered "bad" in a terms of Go's unit testing, but this way is found to be the only fast in development and reliable in results that I've found. – snuk182 Jan 28 '15 at 13:11
  • If your flags are universal across tests and you have a TestHelper package, it might be easier to declare them in its `init()`. – steevee Jan 12 '16 at 09:51
5

The accepted answer, I found wasn't completely clear. In order to pass a parameter to a test (without an error) you must first consume that parameter using the flag. For the above example where gamedir.custom is a passed flag you must have this in your test file

var gamedir *string = flag.String("gamedir.custom", "", "Custom gamedir.")

Or add it to the TestMain

Patryk
  • 22,602
  • 44
  • 128
  • 244
notzippy
  • 468
  • 7
  • 10
4

Note that from Go 1.13, you'll get the following error if you use flag.Parse() in init()

flag provided but not defined: -test.timeout

To fix this, you have to use TestMain

func TestMain(m *testing.M) {
    flag.Parse()
    os.Exit(m.Run())
}

TestFoo(t *testing.T) {}
pkk pmk
  • 41
  • 1