Table of Contents
- Introduction
- Prerequisites
- Setting Up
- Understanding Mocking
- Mocking Frameworks
- Creating a Mock
- Mocking Dependencies
- Testing with Mocks
- Recap
-
Introduction
Welcome to this detailed guide on mocking in Go tests. In this tutorial, we will explore the concept of mocking and how it can be utilized to write effective tests in Go. By the end of this tutorial, you will have a clear understanding of what mocking is, how to create mocks, and how to use them in your test functions.
Prerequisites
Before getting started, it is essential to have a basic understanding of Go programming language and unit testing concepts. Additionally, you should have Go installed on your system and a text editor of your choice to write code.
Setting Up
To follow along with this tutorial, we will create a new Go module. Open your terminal or command prompt and execute the following commands:
$ mkdir mocking-example
$ cd mocking-example
$ go mod init example.com/mocking
Understanding Mocking
Mocking is a technique used in software testing to create dummy objects that mimic the behavior of real objects. It allows us to isolate the code under test by replacing its dependencies with controlled ones. Mocking is particularly useful when testing code that relies on external resources such as databases, web services, or complex algorithms.
Mocking Frameworks
In Go, several mocking frameworks are available to simplify the process of creating and managing mocks. Some popular frameworks include:
For this tutorial, we will be using GoMock as our mocking framework of choice. To install GoMock, run the following command:
$ go get github.com/golang/mock/gomock
Creating a Mock
Let’s start by creating a simple example to demonstrate the creation of a mock. Suppose we have a Printer
interface with a single method Print
that prints a message. We want to test a Logger
struct that depends on this Printer
interface.
Create a new file printer.go
and add the following code:
package main
type Printer interface {
Print(message string)
}
type Logger struct {
printer Printer
}
func (l *Logger) Log(message string) {
l.printer.Print(message)
}
Now, let’s generate the mock for this interface. In the terminal, run the following command:
$ go get github.com/golang/mock/[email protected]
$ go generate ./...
This will generate a mock implementation for our Printer
interface. The mock files will be created in the same package as the original interface.
Mocking Dependencies
To use the mock implementation in our tests, we need to instantiate and inject it into the code under test. Let’s create a test file logger_test.go
with the following code:
package main
import (
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"example.com/mocking/mocks"
)
func TestLogger_Log(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockPrinter := mocks.NewMockPrinter(ctrl)
mockPrinter.EXPECT().Print("hello world")
logger := &Logger{
printer: mockPrinter,
}
logger.Log("hello world")
}
func TestLogger_Log_MockAssertion(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockPrinter := mocks.NewMockPrinter(ctrl)
mockPrinter.EXPECT().Print(gomock.Any())
logger := &Logger{
printer: mockPrinter,
}
logger.Log("any message")
}
func TestLogger_Log_MockAssertError(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockPrinter := mocks.NewMockPrinter(ctrl)
mockPrinter.EXPECT().Print("hello world")
logger := &Logger{
printer: mockPrinter,
}
logger.Log("different message")
}
In the above example, we create a new instance of the mock printer using mocks.NewMockPrinter
. We then set up the expectations for the Print
method using EXPECT().Print(...)
. Finally, we inject the mock printer into the Logger
struct and call the Log
method to trigger the test.
Testing with Mocks
To run the tests, execute the following command in the terminal:
$ go test -v ./...
You should see the test results indicating whether they passed or failed. If all goes well, the tests using the mock should pass, and the test with the mock assertion error should fail.
Recap
In this tutorial, we learned about mocking in Go tests. We understood the purpose of mocking and how it helps in isolating code during testing. We explored the GoMock framework and created a mock for a sample Printer
interface. We also saw examples of injecting mocks into the code under test and setting up expectations for the mock methods. Finally, we executed the tests to verify the functionality of our mocks.
Conclusion
Mocking is a powerful technique for writing effective tests in Go. It allows us to control the behavior of dependent objects and create reliable test scenarios. By using the GoMock framework, you can easily create and manage mocks in your test functions. Remember, effective use of mocking can greatly improve the quality and maintainability of your code.