A quick guide to stubbing dependencies in Gosoftware development go test tdd
Very often in our applications we use third party dependencies to access data. This could be either a database connection, a third party API client or a caching layer. Subsequently, in unit tests, these dependencies need to be stubbed in order to avoid expensive remote calls as well as to have a deterministic test suite.
Go’s interface provides a very elegant way of stubbing these dependencies. With interface, we can substitute the dependencies with stubs when testing, allowing us to control the behaviour of the dependencies.
Stubbing a Redis client
In this example, we’ll look at how we can stub a Redis client, so that we can test our code without having to depend on a real Redis host. We will be working with two of Redis commands:
The Go client function signatures of these two commands are:
To get the actual result of the commands, we would be using the
Result() function implemented by both
StatusCmd, which returns a value and error pair.
In order to create a stub of Redis for our tests, we need to define a proxy using Go’s interface. This interface has 2 signatures which wrap the Redis commands we are interested in.
In this interface, we no longer return either a
StatusCmd, but the result of the command and error if there is any. This interface can now be implemented by either a concrete proxy using a real Redis client or a stub with fixed return values.
The actual Redis client is now wrapped in this proxy implementation, with the relevant wrapper methods. In tests, we could use a stub Redis with a preset data.
Next, we are going to simulate writing a sample user account repository which fetches a user account from Redis based on a given id. We would have a constructor function that injects a Redis proxy as dependency into the repository, then returns a pointer to the repository. The repository would have 2 methods:
UpdateAccount(). The former fetches an account by its id from the repository and the former updates an existing account with new data.
Following a Test-Driven Development approach, we start with a test for the repository, in a file
account_repository_test.go. We first set up a stub Redis proxy which implements the
RedisProxy interface above, along with some fake data.
The first test case checks that
FetchAccount() returns the correct user account with its user data. We start with creating a repository with the fake data above. In the function
createRepositoryWithData(), we inject the stub Redis proxy we created into the repository. The test would then run against the stub Redis proxy. We then assert that the account details are correctly retrieved.
Let’s now implement this method. We start by creating a proxy for Redis, which wraps around the
HMSet() methods. We’ll place this in a file
Next, we create a constructor function for
account_repository.go, which takes a
RedisProxy implementation and returns a pointer to an
AccountRepository with a
The rest of the implementation is simply calling the
HGetAll() method on the
RedisProxy, and mapping it into an
Now that we are done with the first method, let’s write the second test case to check that
UpdateAccount() returns the updated user account.
Again, in the implementation, we simply call
HMSet() on the
RedisProxy, and return the account fetched from Redis, which now would have been updated with the new data.
I did not go into too much detail in testing edge cases and other scenarios, to keep the focus on the stubbing technique. With this basic set up, it is easy to extend the proxy interface to cover other methods of
Redis.client. Similar technique could also be used to stub other dependencies such as an api client or a database. The full code can be found on GitHub. This post is partly inspired by this guide on kafka and go, which inspired me to test the code without relying on a real Redis host.