2017-05-01 68 views
1

我是Golang的新手,在学习语言的同时一直采用TDD方法。我一直在相处,但我发现测试第三方软件包相当笨拙,这使我相信我一直在采取错误的做法。在Golang测试/嘲笑第三方软件包

我在与被嘲讽一个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() 
} 

然后我就可以创建一个实现了相同的接口返回我指定的任何值,特别是对于测试错误处理模拟。

我已经打了一个路障,其中在客户端上perform transactions(MULTI)的特定方法返回属于该包装的另一个接口。我会在这种情况下做什么?自己实现这个接口似乎是不可能的。同样,随着此客户端的使用增长,我自己的实现可能会增长到它实现Redis的整个接口 - 这似乎违背了将此委托给外部依赖项的整个想法。

有没有更好的方式来测试像这样的第三方软件包,用于诸如错误处理之类的事情?

+2

为什么它似乎出了问题你?我认为声明一个由'redis.Pipeline'实现的新接口并且让你的'RedisClient.TxPipeline'方法返回该接口而不是'redis.Pipeline'是完全正确的。 – mkopriva

+0

你最终得出了什么结论?我有一个类似的困境。 – Lansana

+0

@Lansana我的方法基本如下,集成测试涵盖与任何依赖关系的交互。对于非Redis特定的内容,或任何无法通过集成测试进行测试的内容,mkopriva的建议也适用。 – cejast

回答

2

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

比方说,你想要的是一个存储来保存和获取用户,你能想象这样的一个接口:

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 
} 
... 
+0

'UserStore'的实现以及它如何与redis进行交互最终是我想要测试的 - 例如,我想断言如果redis返回错误,则会正确处理该错误。这就是我试图模拟Redis客户端的原因。 – cejast

+0

测试'UserStore'的每个实现需要不使用mocks IMO,而是进行真正的调用,因为即使你正在嘲笑,你也不能确定你的实现在没有模拟的情况下以相同的方式工作(想象PostgreSQL会有多难)。这种方法使您能够灵活地将实际的Redis测试与其他代码隔离开来,这些代码只能用于模拟'UserStore'。 – Asdine

+0

@cejast Asdine的代码示例绝对是您想要去的方式,因为它是一个干净的设计。你仍然可以按照他的建议使用模拟等进行测试。如果你想测试你的代码如何与来自Redis客户端的错误交互,只需从你的模拟客户端返回一个错误。如果你想测试你的代码实际上可以与真实的Redis实例一起工作,你将不得不编写一两个集成测试。 – Gavin