Unit testing 戈朗接口公司;嘲笑

Unit testing 戈朗接口公司;嘲笑,unit-testing,go,Unit Testing,Go,我很难在Go中编写单元测试,因为外部库不公开接口(因此不可模仿),而只公开纯函数。即使是大公司也喜欢,所以我想知道我的方法是否足够好。 如果库提供接口s而不是只包含函数的包来让用户模拟它们,这不是一个好的做法吗 到目前为止,我提出的解决方案是用接口的实现来包装这些包,但这似乎太多工作了 我举了一个例子。我的函数可以是这样的 func AnyFunction() error { sess := session.Get("blabla") // logic in here... }

我很难在Go中编写单元测试,因为外部库不公开接口(因此不可模仿),而只公开纯函数。即使是大公司也喜欢,所以我想知道我的方法是否足够好。
如果库提供
接口
s而不是只包含函数的包来让用户模拟它们,这不是一个好的做法吗

到目前为止,我提出的解决方案是用接口的实现来包装这些包,但这似乎太多工作了

我举了一个例子。我的函数可以是这样的

func AnyFunction() error {
    sess := session.Get("blabla")
    // logic in here...
}
其中session是一个导入的包,它返回一个
struct
。我无法模拟包
会话
。 对于本例,我将编写一个带有实现的
SessionInterface
,该实现在内部调用session

例:


对于我现在的测试,我可以模拟SessionInterface并将其注入到我的代码中,这是一个非常常见的解决方案,在可维护性和可测试性之间取得了很好的平衡

我的想法是,您的代码是针对一个接口编程的,正好有两种实现:

  • “prod”版本即您正在测试的库
  • 测试版本,实现接口的存根
使用这种方法,存根只能沿着接口契约验证您的服务及其依赖关系边界,它几乎不能保证您的组件与存根库的协作/集成。根据我的经验,这种方法非常有效,是我个人的偏好。不过,我发现有1到2个更高级别的测试非常重要,以确保您的组件能够成功初始化库的“prod”版本并与之交互


插头:


我已经写了关于这个问题的文章,你在这里做的事情是正确的,这是语言设计者有意识的决定

Go的理念是,您的代码应该“拥有”这些接口,而不是库。在像C#和Java这样的语言中,库预先定义了它们的接口,而不知道消费者真正需要什么。(可能它们包含的方法太多,也可能太少。)在Go中,因为消费者实际上“拥有”了接口,所以您有权指定在最小的接口中实际需要存在哪些方法,而程序需求的更改意味着您也可以更改接口


现在在这个特定的情况下,在可测试性的函数前面创建适配器似乎是奇怪的,但是考虑另一种选择:如果<代码> session >()(代码)>是接口或结构的一种方法,而不是函数,它将迫使所有库用户实例化一个虚拟对象以调用该方法。并不是每个人都会假装出来——他们更容易说,想(像你一样)编写适配器的消费者有权编写适配器,而那些不想编写适配器的消费者可以很高兴地忽略它们。

“库提供接口不是一个好的做法”-如果不需要的话就不会。在Go中,接口实现是隐式的,因此接口通常是在使用它们的地方定义的,而不是在实现它们的地方定义的。也许本文对您来说很有趣:您也可以简单地使用测试可以更改的函数变量,请看,模拟对于测试来说通常是个坏主意。假货和存根在“测试的辨别力增强”的意义上通常“更好”。
type SessionInterface interface {
    Get(s string) Session
}

type mySessionImpl struct {}
func (me *mySessionImpl) Get(s string) Session {
  return session.Get(s)
}