4 Mocking Approaches for Go
Time to read: 6 minutes
4 Mocking Approaches for Go
Mocking is an essential aspect of unit testing. It allows you to isolate the code you are testing by replacing dependencies with mock implementations. In doing so, it allows you to simulate the behavior of complex or external dependencies. In Go, there are several ways in which we can mock application behavior.
In this tutorial, we'll take a look at why mocking is important and also take a look at some mocking techniques in Go.
Prerequisites
Before we begin, ensure that you have the following:
- An understanding of or prior experience with Go
- Go installed
- Your preferred text editor or IDE
Why mocking is important
There are four key reasons to use mocking:
- Isolation: It allows you to test a unit of code separately from its dependencies
- Deterministic testing: Mocking external services allows you to control the behavior and replies, making testing predictable and repeatable
- Speed: Mocks are faster than real implementations, therefore, testing takes less time overall
- Reliability: Mocks prevent flaky tests from failing owing to problems with external services or the network
Set up your project
Open your terminal and create a directory for your project.
Run the following command to initialize your Go module (and simplify managing dependencies in your project).
Mock with interfaces
In Go, interfaces are a powerful way to abstract dependencies, making it easier to replace them with mocks during testing.
They serve as a contract for the functions a type must implement, along with the parameters which the function accepts, and what the function returns. This allows for greater flexibility and abstraction in how dependencies are handled within a program.
This abstraction facilitates the replacement of actual dependencies with mock implementations during testing, enabling the isolation of the code under test from external systems or services.
Let's say you have a UserService
that interacts with a UserRepository
to fetch user data, as in the following code example. Create a new file in the project's top-level directory, named main.go, and add the following code to it.
You can create a mock implementation of the UserRepository
interface manually for testing as in this example.
Create a new file named main_test.go and add the following code to it:
The code above tests the functionality of the UserService
that interacts with a UserRepository
to fetch user data. It creates unit tests for its GetUser()
method, which retrieves user information based on a provided user ID.
The tests create scenarios to verify how the GetUser()
method behaves. The first scenario tests when the requested user exists in the system. The second scenario tests when the requested user does not exist. To facilitate these tests, a mock implementation of the UserRepository
interface is utilized, allowing controlled simulation of user data retrieval operations.
Run the following command to run the test suite:
You should see the test result in your terminal, similar to the screenshot below.
Use a mocking library
Manually mocking in Go can be tedious, especially for large interfaces. However, mocking libraries like Go Mock can automate this process.
Like other mocking libraries, Go Mock is designed to simplify the process of creating mock objects for unit testing. It automates the creation of these objects, which can significantly speed up the testing process and reduce the amount of boilerplate code needed.
Go Mock, specifically, offers several advantages that make it a valuable tool for automating the creation of mock objects:
- Does not override your modules: This means it doesn't alter the original modules you're testing, thus preserving their integrity and functionality outside of the testing environment.
- Does not require passing modules as function parameters: This simplifies the process of setting up tests by eliminating unnecessary parameters.
- Does not require creating callbacks or wrappers around libraries: This further streamlines the testing process by avoiding the need for additional setup steps.
- Provides a succinct API with a human-readable DSL: This makes it easier to define all possible object operations and interactions, enhancing the readability and maintainability of test code.
Let's go through an example demonstrating how to use Go Mock to generate a mock for the UserRepository
interface.
Install the required dependencies
Install Go Mock, the mockgen tool, and stretchr/testify package. The mockgen
package is used to generate mock code. It works by taking an interface defined in your code as input and generates a corresponding mock object.
In our case, the mockgen
package will be used to generate the MockUserRepository
struct and its methods. The stretchr/testify
is used to enhance the test assertions. In our case, it does that by checking if the UserService
functions behave as expected.
Run the following commands:
Define the UserRepository interface
Here, you’ll need to create the repository
package and define the UserRepository
interface. First, create a new directory named repository:
Then, create a new file named user_repository.go inside the repository directory. After that, in repository/user_repository.go, paste the code below:
Generate the mock
Now, you’ll use the mockgen tool to generate mocks for your interface by running the following command in your terminal:
Define the UserService
Next, you’ll need to create the service
package and define the UserService
struct inside it. So, first, create a new directory named service:
Then, create a new file named user_service.go inside the service directory, and paste the code below into it.
You’ll need to change up some things in the generated code. Replace it with the following code:
Write unit tests using the mock
To write the unit test using mocks, you’ll need a test file to test UserService
. So, create a new file named user_service_test.go inside the service directory. Then paste the code below into the new file:
Run the tests
Now, run the tests using the command below:
You should see output in your terminal similar to the screenshot below.
In this example, you can see how Go Mock simplifies the process of creating and using mock objects for unit testing in Go, which, in turn, makes your tests easier to write and maintain.
Use function callbacks
Another technique for mocking in Go is using function callbacks. This technique allows you to inject custom behavior into your code during testing, which can be particularly useful for mocking functions that do not belong to an interface.
Let’s consider a scenario where UserService
depends on a function to fetch user data. Update main.go to match the code below.
Now, update main_test.go to match the code below. You can use function callbacks to mock the fetchUser()
function during testing:
In this example, the fetchUser()
function is replaced with a custom implementation during testing, allowing you to control the behavior of the GetUser()
method.
If you run the tests again, you should see output similar to the following for main_test.go:
Mock HTTP requests
In addition to interfaces, you often need to mock HTTP requests in Go, especially when developing unit tests for functions that make outbound HTTP calls. This practice allows you to simulate the behavior of external services without actually making network requests, thereby speeding up tests and isolating them from external dependencies.
The net/http/httptest package is useful for this purpose. It provides functionality such as creating a mock HTTP server and generating mock HTTP requests. These features allow you to simulate various scenarios, including different HTTP methods, headers, and body content, and to assert the outcomes of those requests.
With `httptest` you can test your application under different conditions such as handling errors, parsing responses, and interacting with APIs.
Let’s say that UserService
fetches user data from an external API. Update your main.go file with the following:
You can use the httptest package to mock the HTTP server. Update your main_test.go file with the following:
If you run the tests again, one final time, you should see output similar to the following for main_test.go:
In this example, httptest.NewServer()
creates a mock server that responds with predetermined responses. This allows you to test how your UserService
handles different HTTP responses without making real network calls.
Those are 4 ways to mock in Go
In this article, we examined why mocking is important and some Golang mocking techniques. Mocking is a vital technique in unit testing that isolates the code under test by replacing dependencies with mock implementations.
Learning how to write more effective unit tests not only ensures that your code is maintainable but also helps you stand out as a developer.
Temitope Taiwo Oyedele is a software engineer and technical writer. He likes to write about things he’s learned and experienced.
The file icon in the post's main image was created by Freepik on Flaticon.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.