Easy Mocking in Go

Dylan Meeus
3 min readJan 2, 2020

--

One of the lovely things about using Go is that testing can be done with the default toolchain, there’s no need to install things like JUnit. But while running unit tests is easy, that does not mean all code can be easily tested.

For example, being able to Mock code is not always easy. There are libraries out there that help with this such as Moq(https://github.com/matryer/moq). It’s a nice tool, and I recommend checking it out, but you don’t actually need any tool to start mocking. (Although it can make life easier)

But let’s imagine a scenario where you have created a tool that can read data from a file, and provides one method to the user called “contains”, which checks if the data contains a string.

We could design our “memory” struct like this:

type memory struct {}

func (b memory) contains(s string) bool {
data, err := b.readData()
if err != nil {
panic(err)
}
return strings.Contains(data, s)
}

func (b memory) readData() (string, error) {
bs, err := ioutil.ReadFile("file.txt")
if err != nil {
return "", err
}
return string(bs), nil
}

The contains method calls the readData method to get the content of the file, and then checks if this content contains our string. I’m using the filesystem here in the readData call, but you could imagine this being a database call as well. 😉

To unit test this, we can create a small test file:

func TestMemoryContains(t *testing.T) {
b := memory{}
if !b.contains("hello") {
t.Errorf("b should contain hello")
}
}

The pitfall being here that we have depended on the filesystem to work correctly, and that the file contains a certain string. Granted, we’ll probably have a working filesystem though. Either way, the file could change or not be found, causing our system to break. Whilst ideally our unit test only checks that ‘contains’ actually can find a “needle” in a “haystack”.

One way around this, is by creating a struct that contains a function rather than binding a method to it.

type betterMemory struct {
datareader func() (string, error)
}

To avoid the hassle with binding the correct function to the variable each time we create the struct, we can create a small “constructor”.

func NewBetterByteCounter() betterMemory {
return betterMemory{
datareader: readData,
}
}

We now define our contains method and read function as follows:

func (b betterMemory) contains(s string) bool {
data, err := b.datareader()
if err != nil {
panic(err)
}
return strings.Contains(data, s)
}

func readData() (string, error) {
bs, err := ioutil.ReadFile("file.txt")
if err != nil {
return "", err
}
return string(bs), nil
}

The difference from the contains method’s point of view is that datareader is a function stored in a variable that we call. And the readData function is no longer bound to the struct.

For practical uses, nothing is really different at this point. As long as you create betterMemory using the constructor function.

But in our unit test, we could now instantiate the struct using the normal syntax, and pass in a different implementation for datareader.

func TestBetterMemoryContains(t *testing.T) {
b := betterMemory{
datareader: func() (string, error) {
return "helo world! :-)", nil
},
}
if !b.contains("hello") {
t.Errorf("B should contain hello")
}
}

And there we go, we can now mock our datareader in each unit test. We can even have it return errors on purpose to see how contains would handle errors.

If you liked this post and ❤️ Go as well, consider:

--

--