Introduction
Go, also known as Golang, is a programming language designed by Google to be simple, efficient, and scalable. It is widely used for developing web applications, micro-services, and other networked applications. In Go, unit testing is an essential part of the development process. However, testing dependent components can be challenging. This is where Mock testing comes into play.
Mock testing is a technique used to simulate dependencies of a component to test the behaviour of the component under different scenarios. It allows developers to test components in isolation, without having to worry about dependencies. There are various tools available for mock testing in Go, and one of the most popular ones is Mockery.
Mockery is a code generation tool that creates mock objects for Go interfaces. It can generate mocks for both internal and external packages, and it supports various testing frameworks such as Go’s built-in testing package and testify. In this article, we will discuss how to use Mockery to generate mock objects for testing in Go by using testify package.
Installation
The first step is to install Mockery using the following command:
go install github.com/vektra/mockery/v2@v2.20.0
This will install the latest version of Mockery and its dependencies.
For more info on installation: https://vektra.github.io/mockery/installation/
Read mockery documentation: https://vektra.github.io/mockery/
How to use it
Once you have installed Mockery, you can start generating mock objects. Let’s say we have an interface named MyInterface in the file myinterface.go, as follows:
package mypackage
type MyInterface interface {
MyMethod(a int, b string) (int, error)
}
Above code is just to show that we are going to use only MyInterface
for our reference for rest of session. You can refer the full code here for the struct. Struct or implementation can also be in different file also.
package mypackage
type MyInterface interface {
MyMethod(int, string) (int, error)
}
type MyComponent struct {
i MyInterface
}
func (c *MyComponent) MyMethod() (int, error) {
// Call the MyMethod method of the underlying interface
return c.i.MyMethod(42, "hello")
}
func NewMyComponent(i MyInterface) *MyComponent {
return &MyComponent{i: i}
}
To generate a mock object for this interface, we can run the following command:
mockery --name MyInterface
This will generate a file named my_interface_mock.go
in the same directory as the original interface file. The generated file will contain a struct that implements the interface and a constructor function that creates an instance of the struct. Here is an example of what the generated code might look like:
package mypackage
import "github.com/stretchr/testify/mock"
type MyInterfaceMock struct {
mock.Mock
}
func (m *MyInterfaceMock) MyMethod(a int, b string) (int, error) {
args := m.Called(a, b)
return args.Int(0), args.Error(1)
}
func NewMyInterfaceMock() *MyInterfaceMock {
return &MyInterfaceMock{}
}
As you can see, the MyInterfaceMock
struct embeds a mock.Mock
field, which provides the necessary functionality for mocking method calls. The MyMethod method is implemented using the m.Called method, which records the arguments passed to the method and returns the results specified by the test case. Finally, the NewMyInterfaceMock function returns a new instance of the mock object.
Integration with Testing Frameworks
Now that we have generated a mock object, we can use it to test our components. Mockery integrates with various testing frameworks, such as Go’s built-in testing package and testify. Here is an example of how to use the mock object with Go’s testing package:
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/mypackage"
"github.com/mypackage/mocks"
)
func TestMyComponent(t *testing.T) {
// Create a new instance of the mock object
mock := new(mocks.MyInterface)
// Set up the expected behavior of the mock object
mock.On("MyMethod", 42, "hello").Return(100, nil)
// Create an instance of the component under test
c := mypackage.NewMyComponent(mock)
// Call the method being tested
result, err := c.MyMethod()
// Check that the result and error match the expected values
expected_result := 100
expected_error := nil // or whatever error is expected
if result != expected_result || err != expected_error {
t.Errorf("unexpected result or error: got (%v, %v), expected (%v, %v)",
result, err, expected_result, expected_error)
}
// Assert that the expected method was called with the expected arguments
expected_args := []interface{}{42, "hello"}
mock.AssertCalled(t, "MyMethod", expected_args...)
}
In this version, we use the standard library testing package to define a test function named TestMyComponent
. We start by creating a new instance of the mock object using the new function, which allocates a zeroed value of the type of its argument, in this case mocks.MyInterface
. We then set up the expected behaviour of the mock object using the On method, which takes the name of the method being mocked and its arguments, and returns a *mock.Call
value that can be used to specify the return values of the method call.
Next, we create an instance of the component under test using the mypackage.NewMyComponent
function, passing the mock object as an argument. We then call the method being tested and store the result and error values in the result and err variables, respectively.
To check that the result and error match the expected values, we define two variables expected_result
and expected_error
with the values that we expect the method call to return, and use an if statement to compare them to the actual values. If they don't match, we use the t.Errorf
function to report an error with a custom message that includes the actual and expected values.
Finally, we assert that the expected method was called with the expected arguments using the mock.AssertCalled
method, which takes the name of the method being mocked and its expected arguments. If the method was not called with the expected arguments, mock.AssertCalled will fail the test and report an error with a message that includes the actual and expected method calls.
Conclusion
Mock testing is an essential technique for testing components in isolation, without having to worry about dependencies. Mockery is a powerful tool for generating mock objects in Go and it integrates seamlessly with various testing frameworks. By using Mockery to generate mock objects, you can write more comprehensive and effective unit tests for your Go applications.
Top comments (0)