Problem

I’ve been working on a Go CLI app to push sensor data to Cloud™ and hit the problem.

In order to read the sensor data I had a very simple piece of code:

package main //analog_read.go import ( "flag" "fmt" "os" "github.com/hybridgroup/gobot/platforms/intel-iot/edison" ) var ( pin = flag. String ( "pin" , "" , "Sensor's pin-ID to read analog value from. Required." ) ) func main () { flag. Parse () if *pin == "" { flag. PrintDefaults () os. Exit (1) } var ed = edison. NewEdisonAdaptor ( "edison" ) v, err := ed. AnalogRead (*pin) if err != nil { fmt. Fprintln (os.Stderr, err) } fmt. Println (v) }

If the above code runs without pin flag specified then it dumps flag defaults prompting to specify the pin.

But instead of expected prompt:

$ go run analog_read.go -pin string Sensor's pin-ID to read analog value from. Required.

I got completely unexpected prompt:

$ go run analog_read.go -pin string Sensor's pin-ID to read analog value from. Required. -test.bench string regular expression to select benchmarks to run -test.benchmem print memory allocations for benchmarks -test.benchtime duration # # ... and many more #

Somehow flags from testing package got mixed into flags of my app. WTF?

Testing package

testing package is not intended to be used outside of *_test.go files(unless it’s Ok to have side-effects the package produces).

If you look at its source code, it adds the flags I didn’t expect to show up in my little CLI tool:

var matchBenchmarks = flag. String ( "test.bench" , "" , "regular expression to select benchmarks to run" ) var benchTime = flag. Duration ( "test.benchtime" , 1*time.Second, "approximate run time for each benchmark" ) // etc

But why would those flags show up if no code imports the package? Well, because some other code import s the package!!?

Investigation

Luckily I had only single suspect to investigate: Gobot, because there’s no other packages imported aside ones from standard library in my app.

Quickly grepping through Gobot’s sources revealed what non-test code imports testing package:

gobot $ grep -nr '"testing"' ./|grep -v "_test.go" ./gobot/generate.go:317: "testing" ./utils.go:13: "testing"

where,

gobot/generate is not the offender as its "testing" is just a template string.

So, turns out gobot/utils.go (which is not a test) was import ing "testing" and causing problems. Apparently just to introduce test helpers Assert and Refute and enable other packages to re-use them.

Refactoring

Right solution for sharing code that imports "testing" package is to extract it into separate package( gobot/gobottest in this case), so only test code could import it.

Given the fact that a lot of tests depend on Assert and Refute helpers it was a good opportunity to use goftm as a refactor tool:

$ gofmt -r 'gobot.Assert -> gobottest.Assert' $ gofmt -r 'gobot.Refute -> gobottest.Refute'

Did the job and after moving helper code to the new package everything started working as expected!

Great Success!

Conclusion

Do not import "testing" package in your non-test code!