Mock Database Unit Test with Mockery in Go
Tags: tutorial, webdev, programming, go
Published: 2022-02-20 17:03:17

## Introduction
Hi everyone, in this article, we will learn how to create unit test in Go, using mockery to testing a project's dependency.  We often use mock testing as we unit test a function with another dependencies such as database, so we will learn on how to create it by using example that i'd wrote in [here](https://dev.to/yanoandri/database-handling-with-golang-gorm-crud-handling-4c66) with the steps by steps on how to refactor, create unit test and using the mockery itself, let's get start into it!

## Why Mockery?
Mockery provide us with the command to generate mock functions, that we can use for unit testing, so as a results we don't have to write our mock function from scratch, and it will be automatically provided by searching into our interface functions, inside our project's dependencies

To make this tutorial easier, we will prepare to refactor our code from previous article first, create a unit test, run the mockery and start using it in our unit test file in Go

## Refactor
first thing to do, we're going to have a look into our old code, we going to define the interface in it, how do we do it? by creating a new root folder with name of `repository`, from here this is what we call when we use our database dependency. 

```
- config
 |_ config.go
- models
 |_ payment.go
- repository
 |_ payment.go
- test.go
```

we will start by defining the interface in `payment.go` inside our repository folder

```
type IPaymentRepository interface {
	UpdatePayment(id string, payment models.Payment) (models.Payment, error)
	DeletePayment(id string) (int64, error)
	SelectPaymentWIthId(id string) (models.Payment, error)
	CreatePayment(payment models.Payment) (int64, error)
}
```

since we are going to separate the database dependency from the function, we're going to create a struct that act to hold our database dependency value

```
type Repository struct {
	Database *gorm.DB
}
```

then we move all the functions except the main one, inside our `payment.go`, the complete repository will look like this

```
package repository

import (
	"errors"

	"github.com/yanoandri/simple-goorm/models"
	"gorm.io/gorm"
)

type IPaymentRepository interface {
	UpdatePayment(id string, payment models.Payment) (models.Payment, error)
	DeletePayment(id string) (int64, error)
	SelectPaymentWIthId(id string) (models.Payment, error)
	CreatePayment(payment models.Payment) (int64, error)
}

type Repository struct {
	Database *gorm.DB
}

func (repo Repository) UpdatePayment(id string, payment models.Payment) (models.Payment, error) {
	var updatePayment models.Payment
	result := repo.Database.Model(&updatePayment).Where("id = ?", id).Updates(payment)
	if result.RowsAffected == 0 {
		return models.Payment{}, errors.New("payment data not update")
	}
	return updatePayment, nil
}

func (repo Repository) DeletePayment(id string) (int64, error) {
	var deletedPayment models.Payment
	result := repo.Database.Where("id = ?", id).Delete(&deletedPayment)
	if result.RowsAffected == 0 {
		return 0, errors.New("payment data not update")
	}
	return result.RowsAffected, nil
}

func (repo Repository) SelectPaymentWIthId(id string) (models.Payment, error) {
	var payment models.Payment
	result := repo.Database.First(&payment, "id = ?", id)
	if result.RowsAffected == 0 {
		return models.Payment{}, errors.New("payment data not found")
	}
	return payment, nil
}

func (repo Repository) CreatePayment(payment models.Payment) (int64, error) {
	result := repo.Database.Create(&payment)
	if result.RowsAffected == 0 {
		return 0, errors.New("payment not created")
	}
	return result.RowsAffected, nil
}
```

because we move all the functions into our repository folder, we will have to change the way to call our functions, we start to define which database value we want to hold in our struct inside our `test.go` at the `main` function

```
repo := repository.Repository{Database: db}
```

and from there we can use our functions, without putting any external dependency inside our parameters, for example

```
// create a payment
payment := models.Payment{
	PaymentCode: "XXX-1",
	Name:        "Payment for item #1",
	Status:      "PENDING",
}

result, err := repo.CreatePayment(payment)
```

## Create a Unit Test File
Luckily if we use VSCode, we can generate the unit test automatically by pressing `ctrl + p` if you are in windows or `command + p` and you will find the option to generate the functions like the screenshot below

![go generate functions in vs code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vcs2gn60jqpmzw1umsyf.png)

and for the example we will create the test function of `CreatePayment` by blocking the whole functions to generate it

```
package repository

import (
	"testing"

	"github.com/yanoandri/simple-goorm/models"
)

func TestRepository_CreatePayment(t *testing.T) {
	type args struct {
		payment models.Payment
	}
	tests := []struct {
		name    string
		repo    Repository
		args    args
		want    int64
		wantErr bool
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := tt.repo.CreatePayment(tt.args.payment)
			if (err != nil) != tt.wantErr {
				t.Errorf("Repository.CreatePayment() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("Repository.CreatePayment() = %v, want %v", got, tt.want)
			}
		})
	}
}
```

## Mockery
We will install mockery using our go command

```
go install github.com/vektra/mockery/v2@latest
```

after it finishes, we will create our mock functions by using mockery features to generate all of function mocks, by typing this command

```
mockery --all --keeptree
```

notice the `mocks` folder of our project has been installed, and all of the functions has been mocked
![Mocks Folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ake60xtoj8ynhev4fkwa.png)

and this is one example of our `CreatePayment` mock functions
```
// CreatePayment provides a mock function with given fields: payment
func (_m *IPaymentRepository) CreatePayment(payment models.Payment) (int64, error) {
	ret := _m.Called(payment)

	var r0 int64
	if rf, ok := ret.Get(0).(func(models.Payment) int64); ok {
		r0 = rf(payment)
	} else {
		r0 = ret.Get(0).(int64)
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(models.Payment) error); ok {
		r1 = rf(payment)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}
```

## Write our test functions to use mocks
Now let's include our mocked function to our unit test

```
package repository

import (
	"errors"
	"testing"

	mocks "github.com/yanoandri/simple-goorm/mocks/repository"
	"github.com/yanoandri/simple-goorm/models"
)

func TestRepository_CreatePayment(t *testing.T) {
	type args struct {
		payment models.Payment
	}
	tests := []struct {
		name    string
		args    args
		want    int64
		wantErr bool
	}{
		// TODO: Add test cases.
		{
			name: "success_create_payment",
			args: args{
				models.Payment{
					PaymentCode: "payment-code-011",
					Status:      "PENDING",
				},
			},
			want:    1,
			wantErr: false,
		},
		{
			name: "failed_create_payment",
			args: args{
				models.Payment{},
			},
			want:    0,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			repo := &mocks.IPaymentRepository{}
			if !tt.wantErr {
				repo.On("CreatePayment", tt.args.payment).Return(tt.want, nil)
			} else {
				repo.On("CreatePayment", tt.args.payment).Return(tt.want, errors.New("Failed to create payment"))
			}
			got, err := repo.CreatePayment(tt.args.payment)
			if (err != nil) != tt.wantErr {
				t.Errorf("Repository.CreatePayment() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("Repository.CreatePayment() = %v, want %v", got, tt.want)
			}
		})
	}
}
```

notice the repo is missing, it is because we didn't have to use the actual dependencies of database, and we want to mock it in every test session.  And for now we will try to run our test by using command

```
go test ./... -v
```

![Tested Results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/71qg6f6u73zp4h7gj6l9.png)

## Conclusions
This is just one function as an example to use mocks functions in unit test, you can continue to make test for `UpdatePayment`, `DeletePayment` and also `SelectPaymentWIthId`.  Mockery has provide us with easier way to generate our functions and as a results we can test our dependencies without creating the same functions twice from scratch, Hope this tutorial helps, and keep exploring! See ya!

Source:
* [Repo](https://github.com/yanoandri/simple-goorm/tree/mockery-tutorial)