Golang Tutorial - 11 Testing
Tags: go, beginners, tutorial
Published: 2020-07-22 03:03:57

## Software Testing
When creating software, a mistake can be found when before or even after software is released. A mistake that occurs in software could be an error, bug and other similar things. Many potential mistakes in software can be evaluated by testing the software itself. Software testing is not only to make sure that the software can be used effectively without any errors or bugs but also to ensure that the software satisfies the user's requirements.   

## Testing in Go
To perform testing in Go can be done by creating a file with the `_test.go` extension. This file must located in the same package with the object or program that needs to be tested. To create a test in the test file, create a function with the `Test..(t *testing.T)` form. To run a test, use the `go test` command.

Here is the example of simple testing of sum operation:

`main.go`
```go
package main

import "fmt"

func main() {
	fmt.Println("The result of 12 + 14 = ", sum(12, 14))
}

//create a sum function
func sum(x, y int) int {
	return x + y
}

```
`main_test.go`
```go
package main

import "testing"
//create a test
func TestSum(t *testing.T) {
	result := sum(12, 14) //get the result
	expected := 26
	if result != expected {
		t.Error("Expected", expected, "Got", result)
	}
}

```

Output (use `go test`):
```
PASS
ok      review-again/uji        4.023s
```
Another example is if the test is failed.
`main_test.go`
```go
package main

import "testing"

func TestSum(t *testing.T) {
	result := sum(12, 14)
	expected := 36 //change the expected value
	if result != expected {
		t.Error("Expected", expected, "Got", result)
	}
}

```

Output:
```
--- FAIL: TestSum (0.00s)
    main_test.go:9: Expected 36 Got 26
FAIL
exit status 1
FAIL    review-again/uji        4.670s
```
The test can be customized by creating a custom struct that consists of test cases and the expected results.
```go
package main

import "testing"

func TestSum(t *testing.T) {
	//create a custom struct
	type testSample struct {
		data1  int
		data2  int
		answer int
	}

	//create a testcases that consist of testSamples
	testCases := []testSample{
		testSample{12, 14, 26},
		testSample{5, 5, 10},
		testSample{45, 45, 90},
	}

	//run a test for each test case
	for _, v := range testCases {
		result := sum(v.data1, v.data2)
		if result != v.answer {
			t.Error("Expected: ", v.answer, "Got: ", result)
		}
	}
}

```

Output:
```
PASS
ok      review-again/uji        4.055s
```

## Code Coverage
Code coverage is also available in Go, Code coverage measures the coverage of code to make sure the code that is written is useful and to minimize the potential of junk code or unused code. 

Here is the example of a code coverage test:
`fizbuz.go`
```go 
package fizbuz

func FizBuz(i int) string {
	if i%3 == 0 && i%5 == 0 {
		return "FizzBuzz"
	} else if i%3 == 0 {
		return "Fizz"
	} else if i%5 == 0 {
		return "Buzz"
	} else {
		return "null"
	}
}
```
`fizbuz_test.go`
```go
package fizbuz

import "testing"

func TestFizBuz(t *testing.T) {
	result := FizBuz(15)
	if result != "FizzBuzz" {
		t.Error("Expected", "FizzBuzz", "Got", result)
	}
}

```

To test the coverage of a code (in this case is `FizBuz()` function) can be done by these commands:
- `go test -cover`: This command is used to run a coverage test and then print the result to the console.
- `go test -coverprofile c.out`: This command is used to run a coverage test then write the result in the `c.out` file. The `c.out` can be any file.
- `go tool cover -html c.out`: This command is used to run a coverage test and then show the result on the web page. With this command. The coverage of a code can be analyzed by which part of the code is executed or not.

Output (`go test -cover`):
```
PASS
coverage: 28.6% of statements
ok      review-again/uji/fizbuz 4.669s
```

Output (inside `c.out` file):
```
mode: set
review-again/uji/fizbuz/fizbuz.go:3.27,4.26 1 1
review-again/uji/fizbuz/fizbuz.go:4.26,6.3 1 1
review-again/uji/fizbuz/fizbuz.go:6.8,6.21 1 0
review-again/uji/fizbuz/fizbuz.go:6.21,8.3 1 0
review-again/uji/fizbuz/fizbuz.go:8.8,8.21 1 0
review-again/uji/fizbuz/fizbuz.go:8.21,10.3 1 0
review-again/uji/fizbuz/fizbuz.go:10.8,12.3 1 0
```
Output (using `go tool cover -html c.out`):
![Code Coverage](https://dev-to-uploads.s3.amazonaws.com/i/o09db8keclge3ndqqnn1.png)

## Benchmarking in Go
Benchmarking is measuring the performance of software to make sure the software can be used efficiently. Benchmarking in Go can be done by creating a function with  `Benchmark...(b *testing.B)` notation.

Here is the example of benchmarking in Go, in this case, the `SquareRoot()` and `AnotherSquareRoot()` functions are used for benchmarking example.
`main.go`
```go
package simplesqrt

import "math"

//SquareRoot returns square root of number
func SquareRoot(f float64) float64 {
	return math.Pow(f, 0.5)
}

//AnotherSquareRoot return square root of number using math.Sqrt()
func AnotherSquareRoot(f float64) float64 {
	return math.Sqrt(f)
}

```
`main_test.go`
```go
package simplesqrt

import (
	"testing"
)

// Benchmark for SquareRoot() function
func BenchmarkSquareRoot(b *testing.B) {
	for i := 0; i < b.N; i++ {
		SquareRoot(16)
	}
}

// Benchmark for AnotherSquareRoot() function
func BenchmarkAnotherSquareRoot(b *testing.B) {
	for i := 0; i < b.N; i++ {
		AnotherSquareRoot(16)
	}
}

```
Output (use `go test -bench .`):
```
goos: windows
goarch: amd64
pkg: review-again/simplesqrt
BenchmarkSquareRoot-4           200000000                7.63 ns/op
BenchmarkAnotherSquareRoot-4    2000000000               0.44 ns/op
PASS
ok      review-again/simplesqrt 9.401s
```
Based on the output, there are two benchmark results:
- `BenchmarkSquareRoot-4` has done 200000000 operations with 7.63 nano second per operation
- `BenchmarkAnotherSquareRoot-4` has done 2000000000 operations with 0.44 nano second per operation

The output can be explained like this:
```
//name of benchmark func       //num of operations     //execution time
BenchmarkSquareRoot-4           200000000                7.63 ns/op
BenchmarkAnotherSquareRoot-4    2000000000               0.44 ns/op
```
Notice that the `AnotherSquareRoot()` function is more efficient than the `SquareRoot()` function. Because the `AnotherSquareRoot()` function has a smaller number in execution time.

## Additional Tips for Software Testing
- Make sure the positive and negative cases are covered.
- Make sure to perform assertions in each test.

## Notes
- Documentation for the test package can be checked [here](https://pkg.go.dev/testing)

> This is the final part of Golang basic tutorial series in this blog. I hope this Golang basic tutorial series is helpful in learning the Go programming language 😀.

## What's Next?
- Practice by coding, for example, you can solve some problems in online coding exercise websites like [hackerrank](https://www.hackerrank.com/) or other related websites.
- Learn more about Golang's best practices.

I hope this article is helpful to learn the Go programming language. If you have any thoughts or feedback, you can write it in the discussion section below.