How to test the main package functions in golang?

后端 未结 4 1820
情书的邮戳
情书的邮戳 2021-01-03 17:43

I want to test a few functions that are included in my main package, but my tests don\'t appear to be able to access those functions.

My sample main.go file looks li

4条回答
  •  悲&欢浪女
    2021-01-03 18:16

    This is not a direct answer to the OP's question and I'm in general agreement with prior answers and comments urging that main should be mostly a caller of packaged functions. That being said, here's an approach I'm finding useful for testing executables. It makes use of log.Fataln and exec.Command.

    1. Write main.go with a deferred function that calls log.Fatalln() to write a message to stderr before returning.
    2. In main_test.go, use exec.Command(...) and cmd.CombinedOutput() to run your program with arguments chosen to test for some expected outcome.

    For example:

    func main() {
        // Ensure we exit with an error code and log message
        // when needed after deferred cleanups have run.
        // Credit: https://medium.com/@matryer/golang-advent-calendar-day-three-fatally-exiting-a-command-line-tool-with-grace-874befeb64a4
        var err error
        defer func() {
            if err != nil {
                log.Fatalln(err)
            }
        }()
    
        // Initialize and do stuff
    
        // check for errors in the usual way
        err = somefunc()
        if err != nil {
            err = fmt.Errorf("somefunc failed : %v", err)
            return
        }
    
        // do more stuff ...
    
     }
    

    In main_test.go,a test for, say, bad arguments that should cause somefunc to fail could look like:

    func TestBadArgs(t *testing.T) {
        var err error
        cmd := exec.Command(yourprogname, "some", "bad", "args")
        out, err := cmd.CombinedOutput()
        sout := string(out) // because out is []byte
        if err != nil && !strings.Contains(sout, "somefunc failed") {
            fmt.Println(sout) // so we can see the full output 
            t.Errorf("%v", err)
        }
    }
    

    Note that err from CombinedOutput() is the non-zero exit code from log.Fatalln's under-the-hood call to os.Exit(1). That's why we need to use out to extract the error message from somefunc.

    The exec package also provides cmd.Run and cmd.Output. These may be more appropriate than cmd.CombinedOutput for some tests. I also find it useful to have a TestMain(m *testing.M) function that does setup and cleanup before and after running the tests.

    func TestMain(m *testing.M) {
        // call flag.Parse() here if TestMain uses flags
        os.Mkdir("test", 0777) // set up a temporary dir for generate files
    
        // Create whatever testfiles are needed in test/
    
        // Run all tests and clean up
        exitcode := m.Run()
        os.RemoveAll("test") // remove the directory and its contents.
        os.Exit(exitcode)
    

提交回复
热议问题