Go tests are run concurrently!

Go tests are run concurrently in their own context

I ran in a problem while writing go tests... and it looked like some parallelism or at least concurrency issues.

The initialization would run twice and not finish properly the second time. That means it would load a file from a test, a file which is rather bad (read invalid) outside of that one test.

So I looked around to see what was happening and that's how I learned that tests in go run concurrently. It sounds all good unless you want to do integration testing because such require a state which is actually not reachable if your unit tests run concurrently. But why is that?

Well... the fact is that each test runs your main init() function in its own context. So if you have 100 tests, that initialization process will run 100 times. Now imagine what happens when your initialization creates 1,000 goroutines...

And in my case, one of my tests would create a file which ends up being utilized in another test when it's not suppose to be. The default test mechanism creates a context so it protects you against variable mix ups, but not against anything which can only be created once: files on disk, server connections, etc.

This means the ..._test.go is rather limited even if really fast and cool in other ways. As soon as you have tests that make use of external resources, you need to write integration tests instead.

There is one other solution too:

Write one single test, which calls many sub-functions doing the actual work.

You may also be able to run with certain tests running in a concurrent manner and others in that one single big test. Either way, it may be much easier to have a good integration test instead.

For the initialization process, if you want to avoid having it initialized 100 times (once per test, really) then you'll want to consider moving the initialization to your main() function, so it doesn't run when you start your test and then write a MainTest() and call the initialization functions you want from there.

func MainTest(m *test.M) {
    initTest()
    code := m.Run()
    cleanTest()
    return code
}

In this case, you place the fnuctions you want to call to initialize your environment inside the initTest(). If you need to do some cleanup, use the cleanTest() function for that. Both will only be executed once so you can do things that are required only once.

Note that the drawback is that you are not running your tests in an as pristine environment when you use such a method (i.e. the init() being run once per test means you get a clean context for each test!)

Eliminate the concurrency from the initialization process using TestMain()