How I mock unit tests in Golang

chseki

Christian Seki

Posted on March 17, 2021

How I mock unit tests in Golang

First of all I just want you guys to know that I'm not a golang expert, I started to use golang as a hobbyist a few months ago but now every web backend project I use it.

System

Alt Text

Note the arrow direction what means that Domain layer have no Idea about App layer so I promise I'll try to keep this in the code.

That's all ! Lets start as simple as possible to focus on tests it self and maybe further I can add more layers into this system and update this diagram but thats it for now.

Domain Layer

event.go

package domain

import "time"

type Event struct {
    Title           string    
    Date            time.Time 
    Place           string        
    Category        string    
    KeyWords        []string  
}

type EventSaver interface {
    Save(Event) error
}
Enter fullscreen mode Exit fullscreen mode
  1. Event is the struct that represents an event in the system
  2. EventSaver is the interface that will be implemented by someone who wants to save an event somehow, often called as an use case of the system.

Use Cases Layer

For simplicity, I made just one use case named AddEvent Memory that try to save the event and pass the error to who called it.
event.go

package usecases

import (
    "github.com/iamseki/dev-to/domain"
)

type AddInMemoryRepository interface {
    Add(domain.Event) error
}

type AddEventInMemory struct {
    repository AddInMemoryRepository
}

func (usecase *AddEventInMemory) Save(e domain.Event) error {
    err := usecase.repository.Add(e)
    return err
}

func NewAddEventInMemory(r AddInMemoryRepository) *AddEventInMemory {
    return &AddEventInMemory{repository: r}
}

Enter fullscreen mode Exit fullscreen mode

Testing

With the testing built in package we can easily start to write unit tests in go, so lets do it !

Lets focus on this peace of code:

type addEventFakeRepository struct {
    MockAddFn func(domain.Event) error
}

func (fake *addEventFakeRepository) Add(e domain.Event) error {
    return fake.MockAddFn(e)
}

func newAddEventFakeRepository() *addEventFakeRepository {
    return &addEventFakeRepository{
        MockAddFn: func(e domain.Event) error { return nil },
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. addEventFakeRepository struct implements the AddInMemoryRepository interface due to implementation of Add method so we can inject it into add event use case
  2. MockAddFn it's the function used to mock the implementation of AddInMemoryRepository
  3. newAddEventFakeRepository creates an instance of AddEventFakeRepository then mocking the case of success returning error as a nil.

However if the case of success its not desirable, we can set the mock function as follow:

func TestAddEventInMemoryCustom(t *testing.T) {
    r := newAddEventFakeRepository()
    r.MockAddFn = func(e domain.Event) error { 
        // do something diferent here !
    }
    sut := usecases.NewAddEventInMemory(r)
}
Enter fullscreen mode Exit fullscreen mode

Source code of all test in usecases package:
event_test.go

package usecases_test

import (
    "testing"

    "github.com/iamseki/dev-to/domain"
    "github.com/iamseki/dev-to/usecases"
)

type addEventFakeRepository struct {
    MockAddFn func(domain.Event) error
}

func (fake *addEventFakeRepository) Add(e domain.Event) error {
    return fake.MockAddFn(e)
}

func newAddEventFakeRepository() *addEventFakeRepository {
    return &addEventFakeRepository{
        MockAddFn: func(e domain.Event) error { return nil },
    }
}

func TestAddEventInMemorySucceed(t *testing.T) {
    r := newAddEventFakeRepository()
    sut := usecases.NewAddEventInMemory(r)

    err := sut.Save(domain.Event{})
    if err != nil {
        t.Error("Expect error to be nil but got:", err)
    }
}
Enter fullscreen mode Exit fullscreen mode

Considerations

I hope this can be useful for someone, I really enjoy to mock my tests in the way I describe here but I'm not pretty shure about how idiomatic it is so I open to suggestions !

📜 source code

💖 💪 🙅 🚩
chseki
Christian Seki

Posted on March 17, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related