Go tests - How to verify if something was called?
Tags: testing, go, beginners
Published: 2022-04-01 23:17:30

Hello, gophers

I was talking to a friend and remembering some challenges we faced when we start our journey in Golang. For context, we both came from a NodeJS background, a land where there are some tests frameworks like the famous Jest which provide methods like [`.toHaveBeenCalled()`](https://jestjs.io/pt-BR/docs/expect#tohavebeencalled), [`toHaveBeenCalledTimes(number)`](https://jestjs.io/pt-BR/docs/expect#tohavebeencalledtimesnumber) and [`.toHaveBeenCalledWith(...)`](https://jestjs.io/pt-BR/docs/expect#tohavebeencalledwitharg1-arg2-), etc.

On other hand, at the Go world, the native `testing` package suits well for (almost) all scenarios of unit tests, but it does not provide a clear way to do things like verifying if something was called and the plot twist is that **_it doesn't need to_**. As Go provide great idiomatic ways to perform thing like this, we don't need to use any framework.

### Okay, but how to verify something was called in "Go idiomatic way"?

The answer is simple: using interfaces and dependency inversion!

**So, let's talk a bit about dependency inversion**

As a good practice of encapsulation, it's recommended that we use interfaces to abstract types whenever is possible in our code. For example, suppose we want to define structs that represent dogs of specific breeds, like these:

```Go
package dogs

type DogLhasa struct {
	Name string
} 

func (d *DogLhasa) Bark() string {
	return "woof-AUAU"
}

type DogRotweiler struct {
	Name string
}

func (d *DogRotweiler) Bark() string {
	return "woof-woof-ARHHHHHGG"
}
```

And suppose that we have a function that calls the `Bark()` method of a dog passed as an argument. The question is how to define this function? Without using interfaces, we have to do something weird like this:

```Go
func MakeDogLhasaBark(dog DogLhasa) string {
	return dog.Bark()
}

func MakeDogRotweilerBark(dog DogRotweiler) string {
	return dog.Bark()
}
```

It's clear that this doesn't scale well, right? Imagine what a mess it would be if we have to write a function like this for every dog bread we want to add to our code.
To work around this, we must work with interfaces. We define an interface that defines what a dog should be, and every code that deals with dogs can relay up this interface.

```Go
type Dog interface {
	Bark() string
}

func MakeDogBark(dog Dog) string {
	return dog.Bark()
}
```

And now when we call `MakeDogBark`, we can pass whatever object of the type of some struct that implements the interface `Dog`.

```Go
func main() {
	dog := &DogLhasa{Name: "Rex"}
	MakeDogBark(dog)
}
```

A lot simpler, isn't it? This is called **dependency inversion** - instead of depending on some specific dog struct, our function `MakeDogBark` depends on the interface `Dog` that defines what a struct must implement to be a `Dog`. Thus, the `MakeDogBark` function can accept any object of any struct that implements the interface `Dog`. Check this visual representation of dependency inversion:

![Dependency inversion representation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kv4z8qi6afpdtlzf88xt.png)

**But how exactly this can help us to implement tests like the `toHaveBeenCalled.*` ones?**

Let's write a unit test to the function `MakeDogBark`. In this test, we want to ensure that when `MakeDogBark` is called, the `Bark` method of `dog` passed as argument is called. To do this, we can create a "fake dog" struct that allows us to keep track of how many times the method `Bark` was called. Like this:

```Go
type fakeDog struct {
	numberOfBarks int
}

func (d *fakeDog) Bark() string {
	d.numberOfBarks++
	return "woof"
}
``` 

This way, we increase the variable `numberOfBarks` every time an object of type `fakeDog` barks. So, to test if the `Bark` method was called, we just have to check if the `numberOfBarks` is greater than zero.

```Go
func TestMakeDogBark(t *testing.T) {
	dog := &fakeDog{}

	MakeDogBark(dog)

	// fail if the dog didn't bark
	if dog.numberOfBarks == 0 {
		t.Errorf("Expected dog to bark once, but it barked %d times", dog.numberOfBarks)
	}
}
```

Similar heuristics can be used to achieve variations of `toHaveBenCalled.*` tests. For example:

**toHaveBeenCalledWith**
```Go
type fakeStruct struct {
	calledWith string
}

func (f *fakeStruct) SomeMethod(s string) {
	f.calledWith = s
}

func TestSomeMethod(t *testing.T) {
	f := &fakeStruct{}
	f.SomeMethod("hello")
	if f.calledWith != "hello" {
		t.Errorf("Expected SomeMethod to be called with 'hello', but was called with '%s'", f.calledWith)
	}
}
```

**toHaveBeenCalledTimes**
```Go
type fakeStruct struct {
	calledTimes int
}

func (f *fakeStruct) SomeMethod() {
	f.calledTimes++
}

func TestSomeMethod(t *testing.T) {
	f := &fakeStruct{}

	f.SomeMethod()
	f.SomeMethod()
	f.SomeMethod()

	if f.calledTimes != 3 {
		t.Errorf("Expected f.calledTimes to be 3, but it was %d", f.calledTimes)
	}
}
```

Pretty nice, right? It's amazing how much we can achieve with Golang just using native stuff, with no third-party framework or libraries.

Happy testing! 🧪