在Golang中测试/模拟第三方软件包

时间:2017-05-01 18:23:29

标签: testing go tdd

我是Golang的新手,在学习语言的同时一直采用TDD方法。我一直相处得很好但我发现测试第三方软件包非常笨重,这让我相信我一直采取错误的方法。

我遇到问题的具体情况是模仿Redis client进行错误处理。我采用的方法是创建自己的接口,实现包装我想要使用的客户端方法。

type Redis interface {
    Get(key string) (string, error)
}

type RedisClient struct {
    client *redis.Client
}

func (redisClient *RedisClient) New(client *redis.Client) *RedisClient {
    redisClient.client = client

    return redisClient
}

func (redisClient *RedisClient) Get(key string) (string, error) {
    return redisClient.client.Get(key).Result()
}

然后我可以创建一个mock来实现相同的接口,以返回我指定的值,特别是测试错误处理。

我遇到了一个障碍,其中客户端上的特定方法perform transactions(MULTI)返回属于该包的另一个接口。在这种情况下我该怎么办?我自己似乎无法实现该界面。

同样,随着这个客户端的使用增长,我自己的实现可以增长到它实现Redis的整个接口 - 这似乎违背了将这个委托给外部依赖的整个想法。

是否有更好的方法来测试像这样的第三方软件包,例如错误处理?

1 个答案:

答案 0 :(得分:3)

一种方法是创建一个类型,专注于您想要完成的内容,而不是您正在使用的客户端的哪些方法。

让我们说你想要的只是一个存储来保存和获取用户,你可以想象这样的界面:

type UserStore interface {
  SaveUser(*User) error
  GetUserByID(id string) (*User, error)
  SearchUsers(query string) ([]User, error)
}

然后,您可以实现此存储的Redis版本并调用您想要的任何客户端方法,这并不重要。你甚至可以在PostgreSQL中实现一个。 此外,使用此方法可以更轻松地进行模拟,因为您需要做的就是实现此接口而不是Redis。

以下是此界面的模拟版本示例:

type UserStoreMock struct {
  SaveUserFn func (*User) error
  SaveUserInvoked bool
  ...
}

func (m *UserStoreMock) SaveUser(u *User) error {
  m.SaveUserInvoked = true

  if m.SaveUserFn != nil {
    return m.SaveUserFn(u)
  }

  return nil
}
...

然后你可以在这样的测试中使用这个模拟:

var m UserStoreMock

m.SaveUserFn = func(u *User) error {
  if u.ID != "123" {
    t.Fail("Bad ID")
  }

  return ErrDuplicateError
}
...