Creating Unit Tests Using Mock and Coverage Test in Golang — Learning Golang

You,

INTRODUCTION

There are several things to consider when creating unit tests in Golang. For the unit test file, add the suffix _test.go, and place the file in a folder where the code to be tested resides. When writing functions in the _test.go file, add the prefix Test, for example, TestCreateEmployee.

In this unit test tutorial, we will use mocks. What is a mock? A mock is an implementation of an object that we define the output for. In certain conditions, mocks can be useful, such as minimizing execution time when a query process in a specific function takes a long time or if we don’t want to call the database.

For example, here we will create unit test files for Services and Controller using mock objects.

PREREQUISITE

Beforehand, import the mock package and then we will create its mock files.

github.com / stretchr / testify

FOLDER STRUCTURE

--ProjectGO
---pkg
---src
---controller
---EmployeeController.go
---EmployeeController_test.go
---interfaces
---EmployeeRepository.go
---ServicesRepository.go
---mocks
---EmployeeRepository.go
---ServicesRepository.go
---models
---Employee.go
---Response.go
---repository
---EmployeeRepositoryImpl.go
---services
---EmployeeServicesImpl.go
---EmployeeService_test.go
---viewmodels
---Response.go
---main.go

MOCK CODE

First, we will create mocks for the output objects from services and controllers as desired.

package mocks
 
import (
	"../models"
	"github.com/stretchr/testify/mock"
	"go.mongodb.org/mongo-driver/bson"
)
 
type EmployeeRepository struct{
	mock.Mock
}
 
func (m * EmployeeRepository) GetEmployeeRepo(filter bson.M) *models.Response{
	var employees = []models.Employee {
		models.Employee {
			Id: 1,
			Name: "Iron Man",
			Nik: "111",
			Division: "IT Developer",
		},
	}
 
	commonResponse := models.Wrap("200", "Success", employees)
	return commonResponse
}
 
func (m * EmployeeRepository) CreateEmployeeRepo(emp models.Employee) *models.Response{
	commonResponse := models.Wrap("200", "Success", nil)
	return commonResponse
}
package mocks
 
import (
	"../models"
	"github.com/stretchr/testify/mock"
	"go.mongodb.org/mongo-driver/bson"
)
 
type EmployeeServices struct{
	mock.Mock
}
 
func (m * EmployeeServices) GetEmployeeServices(filter bson.M) *models.Response{
	var employees = []models.Employee {
		models.Employee {
			Id: 1,
			Name: "Iron Man",
			Nik: "111",
			Division: "IT Developer",
		},
	}
 
	commonResponse := models.Wrap("200", "Success", employees)
	return commonResponse
}
 
func (m * EmployeeServices) CreateEmployeeServices(emp models.Employee) *models.Response{
	commonResponse := models.Wrap("200", "Success", nil)
	return commonResponse
}

UNIT TEST CODE

In this unit test code, we will compare the expected result with the actual result. Since we are using mock objects, the expected result and actual result will be the same. However, we can also use the actual result from the function being tested without using mocks.

package services
 
import (
	"testing"
	"../models"
	"../mocks"
	"github.com/stretchr/testify/assert"
	"github.com/Sirupsen/logrus"
	"github.com/t-tomalak/logrus-easy-formatter"
	"os"
	"go.mongodb.org/mongo-driver/bson"
)
 
func TestCreateEmployeeRepo(t *testing.T){
 
	logger := &logrus.Logger{
        Out:   os.Stderr,
        Level: logrus.DebugLevel,
        Formatter: &easy.Formatter{
            TimestampFormat: "2006-01-02 15:04:05",
            LogFormat:       "[%lvl%]: %time% - %msg%",
        },
	}
 
	employee := models.Employee{ Id : 3, Name : "Thor", Nik : "113", Division : "IT Developer"}
 
	employeeRepository := new(mocks.EmployeeRepository)
	service := EmployeeServicesImpl{Impl : employeeRepository, Log : logger}
	actualResult := service.CreateEmployeeServices(employee)
 
	expectedResult := models.Wrap("200", "Success", nil)
 
	assert.Equal(t, expectedResult, actualResult)
}
 
func TestGetEmployeeRepo(t *testing.T){
 
	logger := &logrus.Logger{
        Out:   os.Stderr,
        Level: logrus.DebugLevel,
        Formatter: &easy.Formatter{
            TimestampFormat: "2006-01-02 15:04:05",
            LogFormat:       "[%lvl%]: %time% - %msg%",
        },
	}
 
	employeeRepository := new(mocks.EmployeeRepository)
 
	var employees = []models.Employee {
		models.Employee {
			Id: 1,
			Name: "Iron Man",
			Nik: "111",
			Division: "IT Developer",
		},
	}
 
	employeeRepository.On("GetEmployeeRepo", bson.M{"id": 1})
	service := EmployeeServicesImpl{Impl : employeeRepository, Log : logger}
	actualResult := service.GetEmployeeServices(bson.M{"id": 1})
	expectedResult := models.Wrap("200", "Success", employees)
 
	assert.Equal(t, expectedResult, actualResult)
}
package controller
 
import (
	"net/http/httptest"
	"testing"
	"github.com/go-chi/chi"
	"../models"
	"../mocks"
	"../viewmodels"
	"github.com/stretchr/testify/assert"
	"github.com/Sirupsen/logrus"
	"github.com/t-tomalak/logrus-easy-formatter"
	"os"
	"encoding/json"
	"go.mongodb.org/mongo-driver/bson"
)
 
func TestCreateEmployee(t *testing.T){
 
	logger := &logrus.Logger{
        Out:   os.Stderr,
        Level: logrus.DebugLevel,
        Formatter: &easy.Formatter{
            TimestampFormat: "2006-01-02 15:04:05",
            LogFormat:       "[%lvl%]: %time% - %msg%",
        },
	}
 
	employeeServices := new(mocks.EmployeeServices)
	employee := models.Employee{ Id : 1, Name : "Iron Man", Nik : "111", Division : "IT Developer"}
	employeeServices.On("CreateEmployeeServices", employee)
 
	controller := EmployeeController{Impl : employeeServices, Log : logger}
 
	// call the code we are testing
	req := httptest.NewRequest("POST", "http://localhost:8083/CreateEmployee", nil)
	w := httptest.NewRecorder()
 
	r := chi.NewRouter()
	r.HandleFunc("/CreateEmployee", controller.CreateEmployee)
	r.ServeHTTP(w, req)
 
	expectedResult :=
		viewmodels.Response {
			Code: "200",
			Message: "Success",
			Object: nil,
		}
 
	actualResult := viewmodels.Response{}
	json.NewDecoder(w.Body).Decode(&actualResult)
 
	// assert that the expectations were met
	assert.Equal(t, expectedResult, actualResult)
}
 
func TestGetEmployee(t *testing.T){
 
	logger := &logrus.Logger{
        Out:   os.Stderr,
        Level: logrus.DebugLevel,
        Formatter: &easy.Formatter{
            TimestampFormat: "2006-01-02 15:04:05",
            LogFormat:       "[%lvl%]: %time% - %msg%",
        },
	}
 
	employees := []models.Employee {
		models.Employee {
			Id: 1,
			Name: "Iron Man",
			Nik: "111",
			Division: "IT Developer",
		},
	}
 
	employeeServices := new(mocks.EmployeeServices)
	employeeServices.On("GetEmployeeServices", bson.M{"id":1})
	controller := EmployeeController{Impl : employeeServices, Log : logger}
 
	// call the code we are testing
	req := httptest.NewRequest("POST", "http://localhost:8083/GetEmployee", nil)
	w := httptest.NewRecorder()
 
	r := chi.NewRouter()
	r.HandleFunc("/GetEmployee", controller.GetEmployee)
	r.ServeHTTP(w, req)
 
	expectedResult :=
	viewmodels.Response {
		Code: "200",
		Message: "Success",
		Object: employees,
	}
 
	actualResult := viewmodels.Response{}
	json.NewDecoder(w.Body).Decode(&actualResult)
 
		// assert that the expectations were met
	assert.Equal(t, expectedResult, actualResult)
}
 
func TestGetAllEmployee(t *testing.T){
 
	logger := &logrus.Logger{
        Out:   os.Stderr,
        Level: logrus.DebugLevel,
        Formatter: &easy.Formatter{
            TimestampFormat: "2006-01-02 15:04:05",
            LogFormat:       "[%lvl%]: %time% - %msg%",
        },
	}
 
	employees := []models.Employee {
		models.Employee {
			Id: 1,
			Name: "Iron Man",
			Nik: "111",
			Division: "IT Developer",
		},
	}
 
	employeeServices := new(mocks.EmployeeServices)
	employeeServices.On("GetEmployeeServices", bson.M{})
	controller := EmployeeController{Impl : employeeServices, Log : logger}
 
	// call the code we are testing
	req := httptest.NewRequest("POST", "http://localhost:8083/GetAllEmployee", nil)
	w := httptest.NewRecorder()
 
	r := chi.NewRouter()
	r.HandleFunc("/GetAllEmployee", controller.GetAllEmployee)
	r.ServeHTTP(w, req)
 
	expectedResult :=
	viewmodels.Response {
		Code: "200",
		Message: "Success",
		Object: employees,
	}
 
	actualResult := viewmodels.Response{}
	json.NewDecoder(w.Body).Decode(&actualResult)
 
	// assert that the expectations were met
	assert.Equal(t, expectedResult, actualResult)
}
``
 
### RESULT
 
Now, let’s check the coverage of the unit test code we created. Type the following command to check our test coverage.
 
`Unit Test Coverage Percentage`
 
<Image
  src="/images/post3_img1.webp"
  alt="Photo"
  width={1125}
  height={750}
  priority
  className="next-image"
/>
 
`Unit Test Coverage`
 
<Image
  src="/images/post3_img2.webp"
  alt="Photo"
  width={1125}
  height={750}
  priority
  className="next-image"
/>
 
The first command will show us the percentage of coverage from our unit tests. The second command will generate an HTML file for us to see the details of our unit test coverage. From here, we can see which code is covered and which is no
© Izzat.