It goes without saying that a battalion of soldiers is as strong as their weakest link. In this context, a piece of code is free of bugs and other issues as its weakest point. Kent Beck from facebook once visited us at Andela to give a talk on TDD and throughout his session, it was clear that unit testing is critical part of the software development.
Languages like Python provide standardised libraries and frameworks that make it easy to mock methods and functions inside an impure function or method. With python unit tests are pretty easy thus achieving a 100% test coverage is the norm.
Go or Golang is a light weight language that is not C based. Its syntax is pretty easy, I like to refer to golang as a "Pythonized version of Java" due to the similarity of concepts imported from both Java and Python. Golang does not have an official package that supports mocking of methods during unit testing.
Mocking in golang is done with the help of interfaces. Mocking in unit testing is important as it ensure that variables, methods and functions modified outside the scope of the function being tested do not affect the test output.
Here is the implementation of mocking.go
/** mocking.go **/
package main
import "fmt"
type (
// Values defines interface to be used in mocking
Values interface {
GetVolume() int
GetSurfaceArea() int
}
// Measurements defines the measurements used to calculate
// the surface area and volume of a cube or a cuboid.
Measurements struct {
Length int
Width int
Height int
}
)
// GetVolume calculates and returns the volume of a cube or a cuboid
func (config *Measurements) GetVolume() int {
return config.Height * config.Length * config.Width
}
// GetSurfaceArea calculates and returns the surface area of a cube or cuboid
func (config *Measurements) GetSurfaceArea() int {
return (config.Length * config.Width * 2) +
(config.Length * config.Width * 2) +
(config.Height * config.Width * 2)
}
// GetVolumeAndArea fetches and returns the volume and the area
func GetVolumeAndArea(val Values) (int, int) {
return val.GetVolume(), val.GetSurfaceArea()
}
// program execution starts here
func main() {
var (
area, volume int
data = &Measurements{
Length: 1,
Height: 5,
Width: 3,
}
)
volume, area = GetVolumeAndArea(data)
fmt.Printf("Volume : %d , Area : %d \n", volume, area)
}
GetVolumeAndArea
function accepts an interface as its input parameter. This means that any receiver (struct used to create a method) that implements GetVolume
and GetSurfaceArea
also implements the Values
interface.
Mocking of GetVolume
and GetSurfaceArea
is done by passing their customized implementations through the struct that acts as their receiver.
Below is how mocking of GetVolume
and GetSurfaceArea
should be done using interfaces. No extra libraries or frameworks needed.
/** mocking_test.go **/
package main
import (
"strconv"
"testing"
)
// MockTest helps implement our customized GetVolume and GetSurfaceArea
// needed to mock the original implementation in mocking.go
type MockTest struct {
Elem int
}
// GetVolume returns the volume of a cube
func (config *MockTest) GetVolume() int {
return config.Elem * config.Elem * config.Elem
}
// GetSurfaceArea returns the surface area of a cube
func (config *MockTest) GetSurfaceArea() int {
return config.Elem * config.Elem * 6
}
// TestGetVolumeAndArea tests the functionality of GetVolumeAndArea
func TestGetVolumeAndArea(t *testing.T) {
for key, val := range map[MockTest][]int{
MockTest{Elem: 5}: []int{125, 150},
MockTest{Elem: 2}: []int{8, 24},
MockTest{Elem: 1}: []int{1, 6},
} {
t.Run("Cube side length "+strconv.Itoa(key.Elem), func(t *testing.T) {
volume, area := GetVolumeAndArea(&key)
if volume != val[0] {
t.Errorf("Expected volume to be equal to %d but was equal to %d ",
volume, val[0])
t.FailNow()
}
if area != val[1] {
t.Errorf("Expected area to be equal to %d but was equal to %d ",
area, val[1])
t.FailNow()
}
})
}
}
Apart from mocking, interfaces also helps in separating of concerns such that pieces of code/functions/methods that communicate with each other can be made to run independent of each other.
They also help hide the underlying implementations that should not be exposed to the outside world especially when building an API.
For any Clarifications, Corrections or Complements reach out to me via Email on mailto:migwindungu0@gmail.com
Top comments (6)
I'm a bit confused. If you mock a function or method, and test that mock, how can we then say that the test is a test of the original function or method? You're essentially testing the mock, and not the original function or method, no?
He is not testing the mock, he is testing the GetVolumeAndArea function, he just mocks the two functions inside, as he probably has seperate tests for those functions
thanks for clearing that up 😅
haha, it's been lingering there for a while... I stumbled upon it during my own search for answers. I thought I'd let you know.
Nice piece
Thank you sir.