Photo by Digital Buggu from Pexels https://www.pexels.com/photo/colorful-toothed-wheels-171198/

In this article, we will explore how to write testable Go code. Often, we use external dependencies in our code and want to isolate those side effects during testing.

In our example code, we will connect to a database:

We want to test the Bar function but not have to connect to a real database. In order to do so, we must mock the database.

Test stubs are programs that simulate the behaviors of software components (or modules) that a module undergoing tests depends on. https://en.wikipedia.org/wiki/Test_stub

To do so, we must write code that uses dependency injection. This means we will pass an interface to the Bar function that handles interaction with the database.

Dependency injection is a technique whereby one object supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. https://en.wikipedia.org/wiki/Dependency_injection

We will write an interface that handles any interactions with the third party library, use mockery to automatically generate mock implementations of the interface, then use testify to make assertions on the mock object.

1. Interfaces

Interfaces are named collections of method signatures. Interfaces are a key part of Go.

Using an interface instead of directly using the third party code is a great practice because your code is less coupled to that specific dependency.

It is important to define an interface that matches your usage.We want to abstract the dependency away from the Bar function. The interface will declare which operations we need from the database:

In this case, we only need to insert data into a collection. This is a very basic example but you should get very creative with this process. You might only want to only accept a certain type:

Now that we’ve defined the interface, we need to create a default implementation that uses mgo. You could even create more than one implementation if you had multiple database drivers.

Finally, update your function. We will use the concept of dependency injection to provide the DataAccessLayer interface to Bar() instead of using mgo directly.

2. Mockery

Mockery is a tool that automatically generates mock implementations of interfaces.

First, install mockery

go get github.com/vektra/mockery/.../

Then, generate mocks for the interface

$GOPATH/bin/mockery -name DatabaseAccessLayer

3. Testify

Testify is a testing library for making assertions and mocks. You can use it to make assertions on your mocks.

We can use testify to write a basic test using a mock DataAccessLayer. Please refer to the mock and assert documentation on how to use testify to test go code.

4. Wrapping up

Since we modified Bar() to have a DataAccessLayer , you will have to create a MongoDAL to pass to Bar()

Conclusion

Interfaces are the key to writing testable Go code. They allow you to decouple your code from third party libraries, and make it clear which parts of the library your code requires. Mockery and Testify are important tools for Test Driven Development (TDD).