Structuring tests

Many times when writing tests, you will find yourself copying and pasting code. Experienced programmers know that this is never an ideal scenario. To remain DRY (Don't Repeat Yourself), one excellent solution is table-driven tests. Here, the test cases are written as (<complete inputs>, expected output) tuples in a table with a common driver code. Each table entry can sometimes have additional information such as a test name to make the test output easily readable. Here is a good example from the testing code for the fmt package (http://golang.org/pkg/fmt/):

var flagtests = []struct {
in string
out string
}{
{"%a", "[%a]"},
{"%-a", "[%-a]"},
{"%+a", "[%+a]"},
{"%#a", "[%#a]"},
{"% a", "[% a]"},
{"%0a", "[%0a]"},
{"%1.2a", "[%1.2a]"},
{"%-1.2a", "[%-1.2a]"},
{"%+1.2a", "[%+1.2a]"},
{"%-+1.2a", "[%+-1.2a]"},
{"%-+1.2abc", "[%+-1.2a]bc"},
{"%-1.2abc", "[%-1.2a]bc"},
}

func TestFlagParser(t *testing.T) {
var flagprinter flagPrinter
for _, tt := range flagtests {
s := Sprintf(tt.in, &flagprinter)
if s != tt.out {
t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out)
}
}
}

Given a table of test cases, the actual test simply iterates through all table entries and, for each entry, performs the necessary tests. The test code can be written well once (good error messages, and so on) and reused across tests. This structure makes adding new tests a very low-overhead task.

With Go 1.7, the testing package supports sub-tests that can be run in parallel, thereby reducing the total test execution time. For example, the following code runs through in one second, not four:

func TestParallel(t *testing.T) {
tests := []struct {
dur time.Duration
}{
{time.Second},
{time.Second},
{time.Second},
{time.Second},
}
for _, tc := range tests {
t.Run("", func(subtest *testing.T) {
subtest.Parallel()
time.Sleep(tc.dur)
})
}
}

A final word of caution on unit tests. While automated unit tests provide the insurance/guardrails to move fast in terms of development, on many occasions, the very frameworks that we choose to make our lives easier sometimes get in the way. As Daniel Lebrero aptly summarized in his blog (http://labs.ig.com/code-coverage-100-percent-tragedy), one common anti-pattern is writing a huge amount of test harness code for something that is straightforward. This results in code being fragile and tough-to-iterate-on. Every technique/recommendation has a context, and when the recommendation is applied blindly, it can lead to developer frustration and, ultimately, lack of quality.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset