Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
30 Oct 2022 · 7 min read ·Article 19 / 119
Go

How to Create Unit Tests in Golang

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

Unit Testing Using the Go Library

programming is not easy, even the best programmers cannot write programs that work exactly as desired every time. Therefore, an important part of the software development process is testing. Writing tests for our code is a good way to ensure quality and increase reliability.

Go provides a testing package, containing lots of tools for unit testing purposes. In this chapter we will learn about testing, benchmarks, and also testing using testimonials.

Go includes special programs that make writing tests easier, so let’s create some tests for the packages we create in this session. In the 01-math folder create a new file called math_test.go which contains this.

Previously we created a function as follows.

go
1package main
2
3func Average(xs []float64) float64 {
4	total := float64(0)
5	for _, x := range xs {
6		total += x
7	}
8	return total / float64(len(xs))
9}

So that we can create unit tests, we generate them using vscode, it will create a new file math_test.go and generate the TestAverage function with the contents below.

go
 1func TestAverageGenerate(t *testing.T) {
 2	type args struct {
 3		xs []float64
 4	}
 5	tests := []struct {
 6		name string
 7		args args
 8		want float64
 9	}{
10		{
11			name: "must 10",
12			args: args{
13				xs: []float64{10.0, 10.0},
14			},
15			want: float64(10),
16		},
17	}
18	for _, tt := range tests {
19		t.Run(tt.name, func(t *testing.T) {
20			if got := Average(tt.args.xs); got != tt.want {
21				t.Errorf("Average() = %v, want %v", got, tt.want)
22			}
23		})
24	}
25}

Unit Test Program Odd Even

Create a function to determine that the program outputs the input information regarding odd and even numbers.

go
1func GanjilGenap(angka int) string {
2	if angka%2 == 0 {
3		return "genap"
4	}
5	return "ganjil"
6}
Maka kita akan buat unit test-nya seperti ini:
go
 1package math
 2
 3import (
 4	"testing"
 5)
 6
 7func TestAverageGenerate(t *testing.T) {
 8	type args struct {
 9		xs []float64
10	}
11	tests := []struct {
12		name string
13		args args
14		want float64
15	}{
16		{
17			name: "must 10",
18			args: args{
19				xs: []float64{10.0, 10.0},
20			},
21			want: float64(10),
22		},
23	}
24	for _, tt := range tests {
25		t.Run(tt.name, func(t *testing.T) {
26			if got := Average(tt.args.xs); got != tt.want {
27				t.Errorf("Average() = %v, want %v", got, tt.want)
28			}
29		})
30	}
31}
32
33type testpair struct {
34	values  []float64
35	average float64
36}
37
38var tests = []testpair{
39	{[]float64{1, 2}, 1.5},
40	{[]float64{1, 1, 1, 1, 1, 1}, 1},
41	{[]float64{-1, 1}, 0},
42}
43
44func TestAverage(t *testing.T) {
45	for _, pair := range tests {
46		v := Average(pair.values)
47		if v != pair.average {
48		}
49	}
50}
51
52func TestGanjilGenap(t *testing.T) {
53	type args struct {
54		angka int
55	}
56	tests := []struct {
57		name string
58		args args
59		want string
60	}{
61		{
62			name: "test case mengeluarkan ganjil",
63			args: args{
64				angka: 1,
65			},
66			want: "ganjil",
67		},
68		{
69			name: "test case mengeluarkan genap",
70			args: args{
71				angka: 2,
72			},
73			want: "genap",
74		},
75		{
76			name: "test case mengeluarkan -1",
77			args: args{
78				angka: -1,
79			},
80			want: "ganjil",
81		},
82	}
83	for _, tt := range tests {
84		t.Run(tt.name, func(t *testing.T) {
85			if got := GanjilGenap(tt.args.angka); got != tt.want {
86				t.Errorf("GanjilGenap() = %v, want %v", got, tt.want)
87			}
88		})
89	}
90}

Testing for Tube

First, prepare a Tube struct. We will later use the object variables resulting from this struct as testing material.

go
 1package math
 2
 3import "math"
 4
 5type Tabung struct {
 6	Jarijari, Tinggi float64
 7}
 8
 9func (t Tabung) Volume() float64 {
10	return math.Phi * math.Pow(t.Jarijari, 2) * t.Tinggi
11}
12
13func (t Tabung) Luas() float64 {
14	return 2 * math.Phi * t.Jarijari * (t.Jarijari + t.Tinggi)
15}
16
17func (t Tabung) KelilingAlas() float64 {
18	return 2 * math.Phi * t.Jarijari
19}

Then we will create unit tests one by one from the functions we have created. It can be seen as follows.

Unit Test for Volume function

go
 1func TestTabung_Volume(t *testing.T) {
 2	type fields struct {
 3		Jarijari float64
 4		Tinggi   float64
 5	}
 6	tests := []struct {
 7		name   string
 8		fields fields
 9		want   float64
10	}{
11		{
12			name: "testing hitung volume",
13			fields: fields{
14				Jarijari: 7, Tinggi: 10,
15			},
16			want: float64(792.8366544874485),
17		},
18	}
19	for _, tt := range tests {
20		t.Run(tt.name, func(t *testing.T) {
21			tr := Tabung{
22				Jarijari: tt.fields.Jarijari,
23				Tinggi:   tt.fields.Tinggi,
24			}
25			if got := tr.Volume(); got != tt.want {
26				t.Errorf("Tabung.Volume() = %v, want %v", got, tt.want)
27			}
28		})
29	}
30}

Unit Test for Surface Area function

go
 1func TestTabung_Luas(t *testing.T) {
 2	type fields struct {
 3		Jarijari float64
 4		Tinggi   float64
 5	}
 6	tests := []struct {
 7		name   string
 8		fields fields
 9		want   float64
10	}{
11		{
12			name: "testing hitung luas permukaan",
13			fields: fields{
14				Jarijari: 7, Tinggi: 10,
15			},
16			want: float64(385.092089322475),
17		},
18	}
19	for _, tt := range tests {
20		t.Run(tt.name, func(t *testing.T) {
21			tr := Tabung{
22				Jarijari: tt.fields.Jarijari,
23				Tinggi:   tt.fields.Tinggi,
24			}
25			if got := tr.Luas(); got != tt.want {
26				t.Errorf("Tabung.Luas() = %v, want %v", got, tt.want)
27			}
28		})
29	}
30}

Unit test for Base Perimeter

go
 1func TestTabung_KelilingAlas(t *testing.T) {
 2	type fields struct {
 3		Jarijari float64
 4		Tinggi   float64
 5	}
 6	tests := []struct {
 7		name   string
 8		fields fields
 9		want   float64
10	}{
11		{
12			name: "testing hitung keliling alas",
13			fields: fields{
14				Jarijari: 7, Tinggi: 10,
15			},
16			want: float64(22.65247584249853),
17		},
18	}
19	for _, tt := range tests {
20		t.Run(tt.name, func(t *testing.T) {
21			tr := Tabung{
22				Jarijari: tt.fields.Jarijari,
23				Tinggi:   tt.fields.Tinggi,
24			}
25			if got := tr.KelilingAlas(); got != tt.want {
26				t.Errorf("Tabung.KelilingAlas() = %v, want %v", got, tt.want)
27			}
28		})
29	}
30}

The way to execute testing is using the go test command. The -v or verbose argument is used to display all log output at the time of testing.

Run the program as below, it can be seen that none of the tests fail.

bash
 1➜  tabung git:(main) ✗ go test -v
 2=== RUN   TestTabung_Volume
 3=== RUN   TestTabung_Volume/testing_hitung_volume
 4--- PASS: TestTabung_Volume (0.00s)
 5    --- PASS: TestTabung_Volume/testing_hitung_volume (0.00s)
 6=== RUN   TestTabung_Luas
 7=== RUN   TestTabung_Luas/testing_hitung_luas_permukaan
 8--- PASS: TestTabung_Luas (0.00s)
 9    --- PASS: TestTabung_Luas/testing_hitung_luas_permukaan (0.00s)
10=== RUN   TestTabung_KelilingAlas
11=== RUN   TestTabung_KelilingAlas/testing_hitung_keliling_alas
12--- PASS: TestTabung_KelilingAlas (0.00s)
13    --- PASS: TestTabung_KelilingAlas/testing_hitung_keliling_alas (0.00s)
14PASS
15ok      github.com/santekno/tabung      0.486s

Method Test

MethodUses
Log()Display logs
Logf()Displays logs using
Fail()Indicates that Fail() has occurred and the function testing process continues
FailNow()Indicates that Fail() has occurred and the function testing process has stopped
Failed()Display file report
Error()Log() followed by Fail()
Errorf()Logf() followed by Fail()
Fatal()Log() followed by failNow()
Fatalf()Logf() followed by failNow()
Skip()Log() followed by SkipNow()
Skipf()Logf() followed by SkipNow()
SkipNow()Stop the function testing process, proceed to function testing
Skiped()Displays the skip
Parallel()Sets that test execution is parallel

Test Command Notes

Command to view the coverage of unit tests in a project

bash
1➜  tabung git:(main) ✗ go test -coverprofile=coverage.out
2PASS
3coverage: 100.0% of statements
4ok      github.com/santekno/tabung      0.750s

Command to see which code has covered the unit test

bash
1➜  tabung git:(main) ✗ go tool cover -html=coverage.out

Then the result will be ‘generated’ html in the form of a visualization of unit tests that have been covered or not

Command to regenerate using moq

bash
1$ cd <folder-yang-akan-di-generate>
2$ go generate ./...

Also make sure that the interface function is added to the top of struct like this

go
1// go:generate moq -out main_mock_test.go . UserRepositoryInterface

With a rule like this go:generate moq -out <mock-test-file-name> . <struct-interface-to-be-mocked>

Related Articles

💬 Comments