Can I mock a function with pointer parameter which need to be used

14.1k Views Asked by At

Let's say we have a library provide a function Double to double the integer, we use pointer i to get the result value not by return:

package api

type Action interface {
    Double(i *int) error
}

type NUM struct{}

func (n NUM) Double(i *int) error {
    *i *= 2

    return nil
}

in our main function we use this library to do our task. like this:

package app

import (
    "fmt"
    "github.com/hotsnow/api"
)

func main() {
    j := job{a: &api.NUM{}}
    d := j.task(3)
    fmt.Println(3, d)
}

type job struct {
    a api.Action
}

// double me
func (j job) task(i int) int {
    j.a.Double(&i)

    return i
}

Now we need to test the task() function, how can we get the pointer return bye mock the Double function?

Here is the test:

package app

import (
    "github.com/golang/mock/gomock"
    "github.com/hotsnow/mocks"
    "testing"
)

func TestReq(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()

    m := mocks.NewMockAction(ctrl)
    m.EXPECT().Double(gomock.Any()).Return(nil)

    j := job{a: m}
    got := j.task(3)
    if got != 6 {
        t.Errorf("got = %#v; want 6", got)
    }
}

The code here: https://github.com/hotsnow/mock.git (stackoverflow branch)

3

There are 3 best solutions below

0
On

you can use gomock setarg function for this

yourPackage.EXPECT().insert(&pointer).SetArg(0, newPointer)
3
On

It seems you don't have to use gomock to test the task method.

Since you have an interface, why not just create a mock implementation of the interface, for example:

type dummy struct{
    callCount int
}

func (d *dummy) Double(i *int) error {
    d.callCount++
    return nil
}

d := dummy{}
j := job{a: &d}
got := j.task(3)
if d.callCount != 1 {
    // XXX
}
1
On

You can achieve this with the provided Eq() matcher, which internally calls reflect.DeepEqual() on the expected and actual values; as per the documentation for this method:

Pointer values are deeply equal if they are equal using Go's == operator or if they point to deeply equal values.

Say we have a function that depends upon an interface method that takes a pointer parameter:

package resource

type ServiceRequest struct {
    Name  string
    Owner *string // this is a pointer so it can be omitted with `nil`
}

type Model struct {
    // resource model...
}

type ResourceService interface {
    Fetch(req *ServiceRequest) (Model, error)
}

type getResourceHandler struct {
    resourceService ResourceService
}

type GetResourceEvent struct {
    Resource string
    Owner    *string
}

func NewResourceHandler(resourceService ResourceService) *getResourceHandler {
    return &getResourceHandler{resourceService}
}

func (h *getResourceHandler) Handle(event GetResourceEvent) (Model, error) {
    return h.resourceService.Fetch(&ServiceRequest{event.Resource, event.Owner})
}

We can use the Eq() matcher when setting up the expectation against our generated mock of the ResourceService interface:

package test

import (
    "testing"

    "github.com/golang/mock/gomock"
    "github.com/stretchr/testify/assert"

    "github.com/org/repo/internal/mock"
    "github.com/org/repo/internal/resource"
)

func optionalString(str string) *string {
    return &str
}

func Test_GetResourceHandler_ReturnsResultFromService(t *testing.T) {
    resourceName := "my-resource"
    owner := optionalString("Joe Bloggs")
    resourceReq := &resource.ServiceRequest{resourceName, owner}
    event := resource.GetResourceEvent{resourceName, owner}
    model := resource.Model{ /* fields here... */ }

    ctrl := gomock.NewController(t)
    mockResourceService := mock.NewMockResourceService(ctrl)
    handler := resource.NewResourceHandler(mockResourceService)

    mockResourceService.EXPECT().Fetch(gomock.Eq(resourceReq)).Return(model, nil)

    res, err := handler.Handle(event)

    assert.Nil(t, err)
    assert.Equal(t, model, res)
}

If you change the contents of the service request in either the test or the unit under test, you'll see that the test no longer passes. Otherwise, it will pass in spite of the test and the unit under test having their own respective pointers to separate ServiceRequest{} values.