Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ssis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Testing 在Golang测试/模拟第三方软件包_Testing_Go_Tdd - Fatal编程技术网

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

Testing 在Golang测试/模拟第三方软件包,testing,go,tdd,Testing,Go,Tdd,我是刚来Golang的新手,在学习语言的过程中一直采用TDD方法。我一直过得很好,但我发现测试第三方软件包相当笨拙,这让我相信我采取了错误的方法 我遇到的具体问题是模拟错误处理。我所采用的方法是创建自己的接口,实现包装了我想要使用的客户机方法 type Redis interface { Get(key string) (string, error) } type RedisClient struct { client *redis.Client } func (redisC

我是刚来Golang的新手,在学习语言的过程中一直采用TDD方法。我一直过得很好,但我发现测试第三方软件包相当笨拙,这让我相信我采取了错误的方法

我遇到的具体问题是模拟错误处理。我所采用的方法是创建自己的接口,实现包装了我想要使用的客户机方法

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,它实现相同的接口,以返回我指定的值,特别是用于测试错误处理的值

我遇到了一个障碍,客户端上的一个特定方法(MULTI)返回属于该包的另一个接口。在这种情况下我会怎么做?自己实现这个接口似乎是不可能的

类似地,随着这个客户机使用量的增长,我自己的实现可以发展到实现整个Redis接口的程度——这似乎与将其委托给外部依赖项的整个想法背道而驰


对于诸如错误处理之类的事情,有没有更好的方法来测试第三方软件包呢?

一种方法是创建一个类型,该类型侧重于您想要完成的任务,而不是您正在使用的客户端方法

假设您只需要一个存储来保存和获取用户,您可以想象这样一个界面:

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

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

  return ErrDuplicateError
}
...
然后,您可以实现此存储的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
}
...
然后,您可以在以下测试中使用此模拟:

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

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

  return ErrDuplicateError
}
...

为什么你觉得这是不可能的?我认为声明一个由
redis.Pipeline
实现的新接口并让您的
RedisClient.TxPipeline
方法返回该接口而不是
redis.Pipeline
,这是非常好的。您最终得出了什么结论?我也面临类似的困境。@Lansana我的方法基本上如下所示,集成测试涵盖了与任何依赖项的交互。对于Redis的非特定内容,或无法通过集成测试进行测试的内容,mkopriva的建议同样有效。是否愿意分享一个Redis实现的虚拟示例?我最终要测试的是
UserStore的实现以及它与Redis的交互方式,例如,我想断言,如果redis返回了一个错误,那么这个错误就得到了正确的处理。这就是我试图模拟redis客户端的原因。测试
UserStore
的每个实现都不需要使用mock IMO,而是需要进行实际调用,因为即使你在模拟,你也无法确保你的实现在没有mock的情况下也能以同样的方式工作(想象一下使用PostgreSQL会有多困难)。这种方法使您能够灵活地将实际的Redis测试与代码的其余部分隔离开来,而代码的其余部分只能与mock
UserStore
@cejast-Asdine一起使用,因为它是一种干净的设计,所以您肯定会喜欢它的代码示例。您仍然可以按照他的建议使用mock等进行测试。如果您想测试代码如何与Redis客户端的错误交互,只需从模拟客户端返回一个错误。如果您想测试您的代码是否能够与真实的Redis实例一起工作,那么您必须编写一两个集成测试。