Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ginkgo fails for code which has flag parsing #285

Closed
tinygrasshopper opened this issue Aug 18, 2016 · 5 comments
Closed

Ginkgo fails for code which has flag parsing #285

tinygrasshopper opened this issue Aug 18, 2016 · 5 comments

Comments

@tinygrasshopper
Copy link
Collaborator

Ginkgo throws an exception when testing the main package if a main package has a init function which parses flags. Works with go test, following error is see when executing ginkgo:

flag provided but not defined: -ginkgo.seed
Usage of /var/folders/kf/8c0q7c3n6r1d8b4_fp9y4y2w0000gn/T/ginkgo226624707/one.test:
  -test.bench string
        regular expression per path component to select benchmarks to run
  -test.benchmem
        print memory allocations for benchmarks
  -test.benchtime duration
        approximate run time for each benchmark (default 1s)
  -test.blockprofile string
        write a goroutine blocking profile to the named file after execution
  -test.blockprofilerate int
        if >= 0, calls runtime.SetBlockProfileRate() (default 1)
  -test.count n
        run tests and benchmarks n times (default 1)
  -test.coverprofile string
        write a coverage profile to the named file after execution
  -test.cpu string
        comma-separated list of number of CPUs to use for each test
  -test.cpuprofile string
        write a cpu profile to the named file during execution
  -test.memprofile string
        write a memory profile to the named file after execution
  -test.memprofilerate int
        if >=0, sets runtime.MemProfileRate
  -test.outputdir string
        directory in which to write profiles
  -test.parallel int
        maximum test parallelism (default 4)
  -test.run string
        regular expression to select tests and examples to run
  -test.short
        run smaller test suite to save time
  -test.timeout duration
        if positive, sets an aggregate time limit for all tests
  -test.trace string
        write an execution trace to the named file after execution
  -test.v
        verbose: print additional output
  -word string
        a string (default "foo")

Ginkgo ran 1 suite in 751.799863ms
Test Suite Failed

Testfile:

package main_test

import (
    . "github.com/onsi/ginkgo"
    . "github.com/onsi/gomega"
    . "github.com/test/test/one"
)

var _ = Describe("One", func() {
    It("returns foo", func() {
        Expect(GetString()).To(Equal("foo"))
    })
})

main.go

package main

import (
    "flag"
    "fmt"
)

var wordPtr = flag.String("word", "foo", "a string")

func init() {
    flag.Parse()
}

func main() {
    fmt.Println("word:", *wordPtr)
    fmt.Printf(GetString())
}
func GetString() string {
    return "bar"
}

We have attached the entire code example as an attachment, unzip it in $GOPATH/github.com/test/test to use

@henryaj and @tinygrasshopper

one.zip

@onsi
Copy link
Owner

onsi commented Aug 18, 2016

:( Sadly flag.Parse() is a magical global that you can only run once. Any subsequent calls are ignored.

Ginkgo registers its flags in an init and then calls flag.Parse() just before running your tests.

I don't know what Go best practices are but I'm forming the opinion that the right approach is always to flag.Parse() in your main() not in init().

Sometimes you've just got to adapt the code to make it work with Ginkgo. This is one of those, sorry :/

So... you'll have to change your code:

package main

import (
    "flag"
    "fmt"
)

var wordPtr = flag.String("word", "foo", "a string")

func init() {
    //define flags (here or on top of main)
}

func main() {
    flag.Parse()
    fmt.Println("word:", *wordPtr)
    fmt.Printf(GetString())
}
func GetString() string {
    return "bar"
}

@robdimsdale
Copy link
Contributor

@tinygrasshopper thanks for the effort in isolating a reproduction.

A quick google suggests a slight tendency to parse in main.main() rather than init() and that is consistent with my own experience. This advice is often based on the kind of problem you're running into during testing. It's actually never occurred to me to parse flags in init(), and I've never had problems parsing in main.main().

What would you gain from parsing in init()?

@tinygrasshopper
Copy link
Collaborator Author

Thanks for the comments folks.

We inherited a codebase which has this issue. The actual code we are trying to modify doesn't look that easy to change, as the flags are populated all over the place. We are going to keep it as it is for now, and use go test as the test runner.

@dcwangmit01
Copy link

dcwangmit01 commented Mar 30, 2017

@tinygrasshopper I was able to work around this issue. Perhaps this code snippet might help.

var _ = Describe("Cmd", func() {
        It("Should run without arguments", func() {
                // The Ginkgo test runner takes over os.Args and fills it with
                // its own flags.  This makes the cobra command arg parsing
                // fail because of unexpected options.  Work around this.

                // Save a copy of os.Args
                origArgs := os.Args[:]

                // Trim os.Args to only the first arg
                os.Args = os.Args[:1] // trim to only the first arg, which is the command itself

                // Run the command which parses os.Args
                err := cmd.RootCmd.Execute()

                // Restore os.Args
                os.Args = origArgs[:]

                Expect(err).Should(BeNil())
        })
})

@tinygrasshopper
Copy link
Collaborator Author

thanks @dcwangmit01

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants