为了账号安全,请及时绑定邮箱和手机立即绑定

从包函数返回 Mock

从包函数返回 Mock

Go
拉丁的传说 2023-07-26 15:32:31
我对 Go 相当陌生,在编写测试时遇到了一些问题,特别是模拟包函数的响应。我正在为 编写一个包装器库github.com/go-redis/redis。目前它确实只有更好的失败错误,但它会随着 statsd 进一步跟踪而扩展,但我离题了......我创建了以下 go 包package myredisimport (    "time"    "github.com/go-redis/redis"    errors "github.com/pkg/errors")var newRedisClient = redis.NewClient// Options - My Redis Connection Optionstype Options struct {    *redis.Options    DefaultLifetime time.Duration}// MyRedis - My Redis Typetype MyRedis struct {    options Options    client  *redis.Client}// Connect - Connects to the Redis Server. Returns an error on failurefunc (r *MyRedis) Connect() error {    r.client = newRedisClient(&redis.Options{        Addr:     r.options.Addr,        Password: r.options.Password,        DB:       r.options.DB,    })    _, err := r.client.Ping().Result()    if err != nil {        return errors.Wrap(err, "myredis")    }    return nil}我的问题是我想redis.NewClient返回一个模拟。这是我写的测试代码,但它不起作用:package myredisimport (    "testing"    "github.com/go-redis/redis"    "github.com/stretchr/testify/assert"    "github.com/stretchr/testify/mock")type redisStatusCmdMock struct {    mock.Mock}func (m *redisStatusCmdMock) Result() (string, error) {    args := m.Called()    return args.Get(0).(string), args.Error(1)}type redisClientMock struct {    mock.Mock}func (m *redisClientMock) Ping() redis.StatusCmd {    args := m.Called()    return args.Get(0).(redis.StatusCmd)}func TestConnect(t *testing.T) {    assert := assert.New(t)    old := newRedisClient    defer func() { newRedisClient = old }()    newRedisClient = func(options *redis.Options) *redis.Client {        assert.Equal("127.0.0.1:1001", options.Addr)        assert.Equal("password", options.Password)        assert.Equal(1, options.DB)    }}我收到以下错误:cannot use clientMock (type *redisClientMock) as type *redis.Client in return argument。我想我读到我需要模拟 的所有功能,redis.Client以便能够在这种情况下将其用作模拟,但事实真的是这样吗?这似乎太过分了,我应该能够以某种方式做到这一点。我该如何让这个测试工作,或者我是否需要重组我的代码以便更容易编写测试?
查看完整描述

1 回答

?
千巷猫影

TA贡献1829条经验 获得超7个赞

redis.Client是一种结构类型,并且在 Go 中结构类型根本不可模拟。然而 Go 中的接口是可模拟的,所以你可以做的是定义你自己的“newredisclient”函数,而不是返回一个结构,而是返回一个接口。由于 Go 中的接口是隐式满足的,因此您可以定义接口,以便它可以由 redis.Client 开箱即用地实现。


type RedisClient interface {

    Ping() redis.StatusCmd

    // include any other methods that you need to use from redis

}


func NewRedisCliennt(options *redis.Options) RedisClient {

    return redis.NewClient(options)

}


var newRedisClient = NewRedisClient

如果您还想模拟 的返回值Ping(),则需要做更多的工作。


// First define an interface that will replace the concrete redis.StatusCmd.

type RedisStatusCmd interface {

    Result() (string, error)

    // include any other methods that you need to use from redis.StatusCmd

}


// Have the client interface return the new RedisStatusCmd interface

// instead of the concrete redis.StatusCmd type.

type RedisClient interface {

    Ping() RedisStatusCmd

    // include any other methods that you need to use from redis.Client

}

现在*redis.Client不再满足接口RedisClient,因为 的返回类型Ping()不同。redis.Client.Ping()请注意, 的结果类型是否满足 的返回接口类型并不重要RedisClient.Ping(),重要的是方法签名不同,因此它们的类型不同。


要解决此问题,您可以定义一个*redis.Client直接使用并满足新RedisClient接口的瘦包装器。


type redisclient struct {

    rc *redis.Client

}


func (c *redisclient) Ping() RedisStatusCmd {

    return c.rc.Ping()

}


func NewRedisCliennt(options *redis.Options) RedisClient {

    // here wrap the *redis.Client into *redisclient

    return &redisclient{redis.NewClient(options)}

}


var newRedisClient = NewRedisClient


查看完整回答
反对 回复 2023-07-26
  • 1 回答
  • 0 关注
  • 83 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信