GoLang – 11 – Testing

Testing in Go: Writing Tests using the testing package, Benchmarking, Table-driven Tests

Testing is an integral part of the software development lifecycle, ensuring the reliability and correctness of code. In Go, the standard testing package provides a simple and effective way to write tests and benchmarks. This section covers writing tests, benchmarking, and table-driven tests, essential components for robust software development in Go.

Writing Tests using the Testing Package

The testing package in Go provides a framework for writing tests. Each test file should have a corresponding file suffixed with “_test.go”. Tests are written using functions that start with “Test” followed by the function being tested. The function signature should be func TestXxx(*testing.T), where Xxx is the function being tested.

Example of a Simple Test

Let’s consider a simple function that adds two numbers and write a test for it:


package main

import "testing"

func Add(a, b int) int {
    return a + b
}

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
        t.Errorf("Add(2, 3) returned %d, expected %d", result, expected)
    }
}

In this example, we define a function Add to add two numbers. We then write a test function TestAdd to verify that the addition works as expected.

Benchmarking

Benchmarking helps measure the performance of code, especially functions that may be performance-critical. The testing package in Go provides a simple way to write benchmarks.

Writing a Benchmark

To write a benchmark, create a function with the prefix “Benchmark” followed by the function being benchmarked. The function signature should be func BenchmarkXxx(*testing.B), where Xxx is the function being benchmarked.


func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = Add(2, 3)
    }
}

In this example, we create a benchmark function BenchmarkAdd to benchmark the Add function.

Table-driven Tests

Table-driven tests are a technique for writing tests in a structured and data-driven way. They involve creating a table of inputs and expected outputs, then iterating over the table to run tests.

Example of a Table-driven Test

Let’s extend our Add function and write a table-driven test for it:


func Add(a, b int) int {
    return a + b
}

func TestAdd(t *testing.T) {
    tests := []struct {
        a, b     int
        expected int
    }{
        {2, 3, 5},
        {0, 0, 0},
        {-1, 1, 0},
    }

    for _, test := range tests {
        result := Add(test.a, test.b)
        if result != test.expected {
            t.Errorf("Add(%d, %d) returned %d, expected %d", test.a, test.b, result, test.expected)
        }
    }
}

In this example, we define a table of tests with different inputs and expected outputs for the Add function. We then iterate over the table, running the tests and comparing the actual output with the expected output.

Testing, benchmarking, and table-driven tests are crucial practices in Go programming to ensure code correctness, performance, and maintainability. By following these practices, you can write reliable, efficient, and maintainable code in Go.