White box and Black box testing in Go

Stefan Alfbo - Feb 25 - - Dev Community

Go has everything packaged from the beginning to getting started with testing your code. The language prefers conventions over configuration. By adhering to common naming conventions, Go projects minimize the need for configuring project-specific styles or rules. This is something that the tools for testing is using too of course.

To begin the journey with white box and black box testing we will use a simple package, a calculator, defined in the file calculator.go.

package calculator

// Calculator defines a simple calculator with basic operations.
type Calculator struct{}

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

func (c *Calculator) Subtract(a, b int) int {
    return a - b
}

func (c *Calculator) Multiply(a, b int) int {
    return a * b
}

func isZero(n int) bool {
    return n == 0
}

func (c *Calculator) Divide(a, b int) int {
    if isZero(b) {
        panic("division by zero")
    }

    return a / b
}
Enter fullscreen mode Exit fullscreen mode

Our tests will live in other files called, calculator_white_box_test.go and calculator_black_box_test.go, here is a convention that is important. For the testing tool to find our test-files we will need to use the suffix, _test, in the filename. The build tool will also exclude this files in the produced binary.

Before we go any further, lets define in simple terms what we mean with white box and black box testing.

White box testing - This refers to the possibility to test all code in your package, both private and public. Therefore all the internal structures and functions (all source code) are available for the tests.

Black box testing - This approach focus more on the functionality since only the public API of the package you are testing is available. The internal implementation of the package is not considered (or available) during these kind of tests.

Ok, so the calculator_white_box_test.go file could look like this.

package calculator

import (
    "testing"
)

func TestAdd(t *testing.T) {
    c := Calculator{}
    result := c.Add(1, 2)
    if result != 3 {
        t.Errorf("Expected 3, but got %d", result)
    }
}

func TestIsZero(t *testing.T) {
    if !isZero(0) {
        t.Errorf("Expected true, but got false")
    }
    if isZero(1) {
        t.Errorf("Expected false, but got true")
    }
}
Enter fullscreen mode Exit fullscreen mode

The convention here is if the package name in the test file has the same name as the package we are testing, then this can be considered white box testing. This will enable the possibility to use private functions as isZero in the tests. Which can be convenient sometimes.

Let us make a file for the black box testing also, calculator_black_box_test.go.

package calculator_test

import (
    "testing"

    calculator "white_black_box_testing"
)

func TestDivide(t *testing.T) {
    c := calculator.Calculator{}
    result := c.Divide(8, 2)
    if result != 4 {
        t.Errorf("Expected 4, but got %d", result)
    }
}

func TestDivideByZero(t *testing.T) {
    c := calculator.Calculator{}
    defer func() {
        if err := recover(); err == nil {
            t.Errorf("Expected panic for division by zero, but got none")
        }
    }()
    c.Divide(8, 0)
}
Enter fullscreen mode Exit fullscreen mode

Notice that the package name is, calculator_test, in this case. That's why we need to import the calculator package. Because of this we only have access to the public API in that package, and therefore we can only do black box testing. So in this file we would not be able to test the isZero function as we did in the previous test file.

One more convention to notice if you haven't written tests before in Go, is that each test function must start with the prefix, Test, to enable the test tool to see that test. Without it, the test will never run.

This is how my go.mod file looks like for completeness of this example.

module white_black_box_testing

go 1.22.0
Enter fullscreen mode Exit fullscreen mode

Which approach should you use? As always, it depends, however I prefer black box testing in most cases.

If you want to learn more about testing your Go code then I can highly recommend this tutorial, Learn Go with Tests.

Happy testing!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .