🚀 Get 25% off access to all my premium courses - use discount code FUNCMAIN at checkout - view the pricing page now!

Video:

The Testdata Directory for Testing

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

In this video, we’re going to be exploring the wonderful world of test fixtures and how you can incorporate them into your Go tests and gain fame and fortune!

What Are Test Fixtures?

This was a new term for me when I started developing Go applications. I wasn’t entirely sure what it meant, but on further research it seems that test fixtures are effectively files which store some form of state that we need for our tests to run.

In this example, our tests fixtures will be some yaml files that we’ll be referencing in our tests to ensure that our tests run in a repeatable and stable environment.

A Test’s Working Directory

These test fixtures that we’ll be creating are going to live in a special testdata directory within each of our packages.

Now, when we call the go test command, it is going to automatically loop through all of the packages within our project. For each package, it will set the current working directory to the package’s root directory and then execute all tests which follow the form TestXxx(t *testing.T).

An Example

Now that the theory is out of the way, let’s dive into the code and start implementing some tests that will utilize this concept.

Let’s mix things up a bit and create a new directory called yamltohtml which will feature a yamltohtml.go and a yamltohtml_test.go file. This package will be tasked with reading in Yaml files and converting them to HTML files for us.

Let’s start off by validating this working directory concept for our tests by creating a TestPWD test:

package Yaml_test

import (
    "os"
    "testing"
)

func TestPWD(t *testing.T) {
    workingDir, err := os.Getwd()
    if err != nil {
        t.Fail()
    }
    t.Log(workingDir)
}

When we go to run this with the verbose flag, we should see that it’s printing out the absolute path to our yamltohtml package directory:

$ go test ./... -v
...
=== RUN   TestPWD
    Yaml_test.go:13: /Users/elliot/Documents/Projects/TutorialEdge/courses/go-testing-bible/yamltohtml

Implementing a YamlToHTML function

Let’s implement the function that will take our Yaml files and spit out a string containing the generated HTML:

package yamltohtml

import (
	"bytes"
	"html/template"
	"io/ioutil"

	"gopkg.in/yaml.v2"
)

type PageData struct {
	Title string `yaml:"Title"`
	Desc  string `yaml:"Desc"`
	Body  string `yaml:"Body"`
}

func YamlToHTML(path string) (string, error) {
	tmpl, err := template.New("page").Parse(`<html><head><title>{{.Title}}</title></head><body>{{.Body}}</body></html>`)
	if err != nil {
		return "", err
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		return "", err
	}

	var pageData PageData

	err = yaml.Unmarshal(data, &pageData)
	if err != nil {
		return "", err
	}

	var tpl bytes.Buffer
	if err := tmpl.Execute(&tpl, pageData); err != nil {
		return "", err
	}

	return tpl.String(), nil
}

Now that we have our new function in place, let’s set about trying to test this. In order to do this, we’ll need to set up a few text fixtures.

The Testdata Directory

Now, within the yamltohtml package directory, let’s create a new directory called testdata which will host the files we’ll be using to test our new function.

Within this directory, let’s create a new yaml file called test_01.yml:

---
Title: "My Awesome Page"
Desc: "My description"
Body: |
    This is my awesome body

Now, let’s try and create a test that will read this test_01.yml file and convert it to a HTML string.

package yamltohtml_test

import (
	"testing"

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

type TestCase struct {
	desc     string
	path     string
	expected string
}

func TestYamlToHTML(t *testing.T) {
	testCases := []TestCase{
		TestCase{
			desc:     "Tests title is set properly",
			path:     "testdata/test_01.yml",
			expected: "<html><head><title>My Awesome Page</title></head><body>This is my awesome body</body></html>",
		},
		TestCase{
			desc:     "Tests body is set properly",
			path:     "testdata/test_02.yml",
			expected: "<html><head><title>My Second Page</title></head><body>This is my awesome body</body></html>",
		},
	}

	for _, test := range testCases {
		t.Run(test.desc, func(t *testing.T) {
			result, err := yamltohtml.YamlToHTML(test.path)
			if err != nil {
				t.Fail()
			}

			t.Log(result)

			if result != test.expected {
				t.Fail()
			}
		})
	}
}

Perfect, with this in place, our tests should successfully reference the 2 test yaml files we have created that live within the testdata directory.

Conclusion

Awesome, so in this tutorial, we looked at the concept of test fixtures within the testdata directory and how you could reference these test fixtures within your own Go tests.

In the next video in this course, we’ll be looking at how you can effectively create a suite of benchmarks so that you can better understand the performance of your code and how it handles a huge number of calls per second.