Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TestMain for all tests?

Tags:

go

I have a fairly large project with many integration tests sprinkled throughout different packages. I'm using build tags to separate unit, integration and e2e tests.

I need to do some setup before running my integration and e2e tests, so I put a TestMain function in a main_test.go file in the root directory. It's pretty simple:

//go:build integration || e2e
// +build integration e2e

package test

import (
  ...
)

func TestMain(m *testing.M) {
  if err := setup(); err != nil {
    os.Exit(1)
  }

  exitCode := m.Run()

  if err := tearDown(); err != nil {
    os.Exit(1)
  }

  os.Exit(exitCode)

}

func setup() error {
  // setup stuff here...
  return nil
}

func tearDown() error {
  // tear down stuff here...
  return nil
}

However, when I run test:

$ go test -v --tags=integration ./...

testing: warning: no tests to run
PASS

# all of my subdirectory tests now run and fail...

I really don't want to write a TestMain in each package that requires it and was hoping I could just get away with one in the root. Is there any solution that you could suggest? Thanks.

The only alternative I can think of is setting up everything outside of code and then running the integration tests. Maybe some shell script that does setup and then calls $ go test?

like image 996
Adam Avatar asked Jun 28 '26 23:06

Adam


2 Answers

The go test ./... command compiles a test binary for each package in the background and runs them one by one. This is also the reason you get a cannot use -o flag with multiple packages error if you attempt to specify an output. This is the reason code in your main package doesn't effect your sub packages.

So the only way to get this to work is to put all your setup logic in sort of "setup" package and call the shared code from all of your sub-packages(still a lot of work, I know).

like image 76
Dylan Reimerink Avatar answered Jul 01 '26 19:07

Dylan Reimerink


Trying to avoid code repetition, I used a function that makes the setup/teardown and evaluates a function as a test.

The function should look like this

func WithTestSetup(t *testing.T, testFunction func()) {
    // setup code

    testFunction()

    // teardown code
}

I use the t *testing.T argument to report errors in setup or teardown, but it can be omitted.

Then in your tests you can make:

func TestFoo(t *testing.T) {
    WithTestSetup(
        t, func() {
            if err := Foo(); err != nil {
                t.Fatal(err)
            }
        },
    )
}

Just call WithTestSetup if needed, looks easier for me than add a bunch of TestMains on the project.

like image 21
Enmanuel Verdesia Avatar answered Jul 01 '26 21:07

Enmanuel Verdesia