Parallel tests in Go

Oct 8, 2019

A great feature of Go is the ability to run tests in parallel using the default testing package.

Here's an example of a very basic mock function with 2 unit tests:

package main

import (
    "errors"
    "testing"
    "time"
)


func mock(input interface{}) error {
	// simulate stuff happening
	time.Sleep(1 * time.Second)

	if input == nil {
		return errors.New("input must not be nil")
	}
	return nil
}

func TestMockNoError(t *testing.T) {
	if err := mock(struct{}{}); err != nil {
		t.Errorf("expected no error, got: %v", err)
	}
}

func TestMockError(t *testing.T) {
	if err := mock(nil); err == nil {
		t.Errorf("expected error, got: %v", err)
	}
}

And as expected, the tests took a total of 2 seconds to run:

$ go test -v main_test.go
=== RUN   TestMockNoError
--- PASS: TestMockNoError (1.00s)
=== RUN   TestMockError
--- PASS: TestMockError (1.00s)
PASS
ok      command-line-arguments  2.003s

Now here's the fun part. We can add a single line of code to both tests to allow them to run in Parallel:

func TestMockNoError(t *testing.T) {
	t.Parallel()
	...
}

func TestMockError(t *testing.T) {
	t.Parallel()
	...
}

We now see a noticable improvement as both tests take only 1 second to run:

$ go test -v main_test.go
=== RUN   TestMockNoError
--- PASS: TestMockNoError (1.00s)
=== RUN   TestMockError
--- PASS: TestMockError (1.00s)
PASS
ok      command-line-arguments  1.002s

Using t.Parallel() is a great way to test your applications for race conditions without the need to manually bootstrap test functions to run in separate goroutines. Not only that, but it also significantly speeds up the run time of your test suites.

As a bonus, this function can also be called inside subtests with the difference being a parent test will only complete once all of its subtests complete:

func TestMock(t *testing.T) {
	t.Run("TestMockNoError", func(t *testing.T) {
		t.Parallel()
		...
	})
	t.Run("TestMockError", func(t *testing.T) {
		t.Parallel()
		...
	})
}

Resources