DEV Community

Cover image for Go: Write Mock Test cases using Mockery and
Ankit malik
Ankit malik

Posted on

Go: Write Mock Test cases using Mockery and

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

Enter fullscreen mode Exit fullscreen mode

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)
}

Enter fullscreen mode Exit fullscreen mode

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}
}

Enter fullscreen mode Exit fullscreen mode

To generate a mock object for this interface, we can run the following command:

mockery --name MyInterface
Enter fullscreen mode Exit fullscreen mode

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{}
}

Enter fullscreen mode Exit fullscreen mode

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...)
}

Enter fullscreen mode Exit fullscreen mode

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)