How can I assign a value to an interface argument using `DoAndReturn` in GoMock?

I have the following method in my RedisClient:

```go
func (r *RedisClient) GetStruct(ctx context.Context, key Key, value interface{}) (*time.Duration, error) {
    // stuff happens here
}

And the corresponding interface and mock:

type Client interface {
    GetStruct(ctx context.Context, key Key, value interface{}) (*time.Duration, error)
}

I want to test this method using GoMock’s DoAndReturn and assign a value to the value argument. My current attempt:

s.mockRedisClient.EXPECT().
    GetStruct(gomock.Any(), cache.BadgesAll, gomock.AssignableToTypeOf(&models.Badge{})).
    DoAndR
eturn(func(ctx context.Context, key cache.Key, value any) (*time.Duration, error) {
        value = []*models.Thing{{ID: 1}}
        duration := time.Minute * 1
        return &duration, nil
    })
````Preformatted text`

However, back in the controller I’m testing, value is always nil. What am I missing here, and how can I properly assign a value to the interface argument using DoAndReturn?

In Go, assigning to the argument inside your DoAndReturn function doesn’t change the caller’s variable because interface{} is passed by value. You need to cast it to the actual pointer type and modify it directly:

s.mockRedisClient.EXPECT().
    GetStruct(gomock.Any(), cache.BadgesAll, gomock.AssignableToTypeOf(&models.Badge{})).
    DoAndReturn`(func(`ctx c``ontext.Context`, key `cache.K`ey, value interface{}) (*time.Duration, error) {
        if v, ok := value.(*[]*`models.Thing`); ok {
            *v = []*`models.Thing`{{ID: 1}}
        }
        duration := time.Minute
        return &duration, nil
})

This way, the caller sees the updated value because you’re dereferencing the pointer that was passed in.

I ran into this issue before: in Go, even though the argument is an interface{}, it wraps a copy of the pointer, not the original variable. So value = … just reassigns the local copy. Using a type assertion and dereferencing works reliably:

DoAndReturn(func(ctx context.Context, key cache.Key, value interface{}) (*time.Duration, error) {
    things := value.(*[]*models.Thing)
    *things = append(*things, &models.Thing{ID: 1})
    d := time.Minute
    return &d, nil
})

It feels subtle, but once you understand Go’s interface semantics, it clicks.

Another approach I often use is defining the interface argument as a pointer to a concrete type in the call site. Then your DoAndReturn can safely mutate it without type assertion magic:

var badges []*models.Thing
s.mockRedisClient.EXPECT().
    GetStruct(gomock.Any(), cache.BadgesAll, &badges).
    DoAndReturn(func(ctx context.Context, key cache.Key, value interface{}) (*time.Duration, error) {
        *value.(*[]*models.Thing) = []*models.Thing{{ID: 1}}
        d := time.Minute
        return &d, nil
    })

This makes tests cleaner and avoids the common mistake of thinking value = … would update the caller.