Testing 在Golang测试/模拟第三方软件包
我是刚来Golang的新手,在学习语言的过程中一直采用TDD方法。我一直过得很好,但我发现测试第三方软件包相当笨拙,这让我相信我采取了错误的方法 我遇到的具体问题是模拟错误处理。我所采用的方法是创建自己的接口,实现包装了我想要使用的客户机方法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
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测试与代码的其余部分隔离开来,而代码的其余部分只能与mockUserStore
@cejast-Asdine一起使用,因为它是一种干净的设计,所以您肯定会喜欢它的代码示例。您仍然可以按照他的建议使用mock等进行测试。如果您想测试代码如何与Redis客户端的错误交互,只需从模拟客户端返回一个错误。如果您想测试您的代码是否能够与真实的Redis实例一起工作,那么您必须编写一两个集成测试。