Video:

Getting Started with TestMain

December 6, 2020

Course Instructor: Elliot Forbes

Hey Gophers! My name is Elliot and I'm the creator of TutorialEdge and I've been working with Go systems for roughly 5 years now.

Twitter: @Elliot_f

As your services become more intricate and complex, so too does your test setup and teardown. Thankfully, this is where the TestMain function can help save the day.

If you need a particular bit of code to execute once within a given test package then you can utilize the TestMain function to handle this execution.

A Simple Implementation

Let’s start off with a simple implementation that will highlight how this works, then when we have the basics down we can build on top of this a little and get a better indication as to how we can use this within our own testing strategies.

ackage yamltohtml_test

import (
	"fmt"
	"os"
	"testing"

	"github.com/TutorialEdge/go-testing-bible/yamltohtml"
)

type TestCase struct {
	desc     string
	path     string
	expected string
}

func TestMain(m *testing.M) {
	fmt.Println("Hello World")
	ret := m.Run()
	fmt.Println("Tests have executed")
	os.Exit(ret)
}

... 

Let’s try run this now in the terminal:

$ go test yamltohtml/yamltohtml_test.go -v
Hello World
=== RUN   TestYamlToHTML
=== RUN   TestYamlToHTML/Test_Case_1
    TestYamlToHTML/Test_Case_1: yamltohtml_test.go:45: <html><head><title>My Awesome Page</title></head><body>This is my awesome content</body></html>
=== RUN   TestYamlToHTML/Test_Case_2
    TestYamlToHTML/Test_Case_2: yamltohtml_test.go:45: <html><head><title>My Second Page</title></head><body>This is my awesome content</body></html>
--- PASS: TestYamlToHTML (0.00s)
    --- PASS: TestYamlToHTML/Test_Case_1 (0.00s)
    --- PASS: TestYamlToHTML/Test_Case_2 (0.00s)
PASS
Tests have executed
ok      command-line-arguments  3.103s

You

An Alternative Approach Using Subtests

Now, there is an alternative approach you could consider which is defaulting to using subtests wrapped in Test functions and handling initialization at the parent test level.

Let’s look at an example:

package _test

func TestCalculate(t *testing.T) {
    client := Client{
        DB: "localhost"
    }

    fmt.Println("Parent Test Level")
    t.Run("My Test 1", func(t *testing.T) {
        // does something with client
        fmt.Println("Test Case 1")
    })

    t.Run("My Test 2", func(t *testing.T) {
        // does something with client
        fmt.Println("Test Case 2")
    })
}

What happens when we execute this?

$ go test ./... -v

This approach does work when you need your initialization code to be ever so slightly different, however if you need the initialization code to only run once then the better candidate is definitely delegating this logic to your TestMain function.

Conclusion

In this tutorial, we looked at how you can simplify your lives by using TestMain to handle all of the setup for your test suites. In the next tutorial in this course, we’ll be looking at the best practices around object comparisons in Go and how you can check for equality using the reflect package.