Testing is a very vital phase/stage in the Software Development Life Cycle (SDLC). In simple terms, it can be defined as writing a block of code to verify the behaviour of another code block. This is to ensure that the code block being tested works as expected, that is, it returns the expected response or output for every given case or set of inputs.

Jest, Chai, Mockito, Pytest, JUnit and Django rest framework’s test library are among some of the test libraries I have worked with but in this post, we’ll be looking at writing tests in Go. According to the official Go website, ‘Go is an open source compiled programming language designed at and supported by Google. It is easy to learn and get started with and has a built-in concurrency and robust standard library’. A great resource to get started with Go is Go Tour which has a list of sequential modules to get you up to speed with the language. In addition to that, Uber Go Style Guide is a helpful resource to guide you on the Go conventions used at Uber.

The Go programming language comes with a testing package, testing, which can be used to carry out various types of tests. It’s usage is intuitive and easy to get started with. We’d be looking at a few examples of its usage. The command used to run tests in go is go test.

Let’s start with the basic example of a function that returns the sum of two integers. In the spirit of Test-Driven Development (TDD), let’s begin with the test.

// sum_ints_test.go

package main

import "testing"

type Params struct {
	A, B int
}

type Testpair struct {
	values Params
	sum    int
}

// testpairs with their corresponding sum
var tests = []Testpair{
	{Params{1, 2}, 3},
	{Params{28, 76}, 104},
	{Params{-86, 25}, -61},
}

func TestSumInts(t *testing.T) {

	for _, pair := range tests {
		v := SumInts(pair.values.A, pair.values.B)

		if v != pair.sum {
			t.Error(
				"For", pair.values, "expected", pair.sum, "got", v,
			)
		}
	}
}

Next, we write the function that performs the addition.

// sum_ints.go

package main

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

In sum_ints.go, the SumInts function takes in two integers as input and returns their sum.

In sum_ints_test.go, we first declare a package (which is a requirement on the first line in every go file), main, and then we import Go’s in-built testing package. We then define a Params struct to model the input parameters of the SumInts function. A Testpair struct is also defined to model a set of input values and their expected sum; this will be used in the assertions. We define a tests variable, which is a slice of the Testpair struct containing parameter values and the expected sum.

Next, we define the TestSumInts test function in which the SumInts function will be tested. In this test function, we iterate over tests and in each iteration the SumInts function is invoked passing the value of the Params struct as input. The result is assigned to a variable v. An assertion is then made to compare the equality of the variable v (which is the result of the SumInts function) and the expected value in the tests struct. If they are equal, that test set passes but if not an error is returned with a message stating the expected value and the actual vaue received. This approach is used in most unit tests. Below is another example.

// imp_interface_test.go

package main

import "testing"

func TestCircleArea(t *testing.T) {

	// assert expected circle area

	var circle Shape = Circle{radius: 3.5}
	expectedCircleArea := 38.48451000647496

	if circle.area() != expectedCircleArea {
		t.Error(
			"Expected ", expectedCircleArea,
			" but got ", circle.area(),
		)
	}
}

func TestRectangleArea(t *testing.T) {

	// assert expected rectangle area

	var rect Shape = Rectangle{length: 8.0, width: 4.0}
	expectedRectangleArea := 100.53096491487338

	if rect.area() != expectedRectangleArea {
		t.Error(
			"Expected ", expectedRectangleArea,
			" but got ", rect.area(),
		)
	}

}
// imp_interface.go

package main

import (
	"math"
)

type Shape interface {
	area() float64
}

type Circle struct {
	radius float64
}

type Rectangle struct {
	length float64
	width  float64
}

// Circle implements the Shape interface
func (c Circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

// Circle implements the Shape interface
func (r Rectangle) area() float64 {
	return math.Pi * r.length * r.width
}