TestMain

I almost missed an awesome new feature in Go 1.4, but thankfully Justinas Stankevičius caught it and wrote a great blog post about it. Of course I’m talking about the new TestMain functionality in the test package. I encourage you to check out Justinas’ post for all the details.

How it works

I’ve done a lot of Django development in the past, and when writing unit tests you leverage the setUp and tearDown methods a lot for preparing fixtures or cleaning up. Go’s TestMain enables you to do this, but for an entire package (not just a single test). So it’s actually closer to Python’s setUpClass and tearDownClass workflow. Here’s a few things to remember when using TestMain:

  1. You can define this function in your test file: func TestMain(m *testing.M).
  2. Since function names need to be unique in a given package, you can only define the TestMain once. If your package has multiple test files, choose a logic place for your single TestMain function.
  3. The testing.M struct has a single defined function named Run(). As you can guess, it runs all the tests within the package. Run() returns an exit code that can be passed to os.Exit.
  4. A minimal implementation looks like this: func TestMain(m *testing.M) { os.Exit(m.Run()) }.
  5. If you do not call os.Exit with the return code, your test command will return 0. Yes, even if a test fails!

Example

So if you add the minimal implementation as defined above, your tests will continue to run exactly as they used to. The real power comes with things you can do before and after you run your tests.

package somepackagename

import (
    "testing"
    "os"
)

func TestMain(m *testing.M) {
    log.Println("This gets run BEFORE any tests get run!")
    exitVal := m.Run()
    log.Println("This gets run AFTER any tests get run!")

    os.Exit(exitVal)
}

func TestOne(t *testing.T) {
    log.Println("TestOne running")
}

func TestTwo(t *testing.T) {
    log.Println("TestTwo running")
}

Now if you run go test, you will see this output:

2014/12/29 12:48:32 This gets run BEFORE any tests get run!
2014/12/29 12:48:32 TestOne running
2014/12/29 12:48:32 TestTwo running
PASS
2014/12/29 12:48:32 This gets run AFTER any tests get run!

This will come in handy for creating some dummy data, or switching databases to point at a test instance. Just remember, the code before and after m.Run() only gets called once! So you may need to clear out a few database tables or files in your /tmp folder after each test. See Justinas’ post for a good example of setting up a test database and clearing it in every test.