我试图避免使用接口,因为我的用例不需要它(类似地在https://www.ardanlabs.com/blog/2016/10/avoid-interface-pollution.html中提到)。但是,要创建一个模拟(使用testify
),我不需要一个接口来模拟吗?我可以创建一个,但这似乎很乏味。使用我的代码的所有地方都需要编写接口来模拟,而不是实际使用。有解决方法吗?
3 回答
如果接口的唯一可能用途是用于测试,我同意,那可能是一个糟糕的接口,你应该避免这种情况。
最好的方法是重构您的系统,使其依赖于一个小的、有用的接口,而不是过度复杂结构的一次性“模拟”。一个很好的例子就是net.Listener
界面。建立在其之上的系统net.Listener
很容易模拟,是的,但这不是你实现net.Listener
. 您使用该接口是因为它允许您交换许多可能的实现,其中之一恰好用于测试。
另一种强大的方法是将功能链接在一起,而不是将功能硬编码为方法。http.HandlerFunc
是一个很好的例子,它也展示了出色的界面设计。查看返回一个http.Handler
而不是一个巨大的“处理程序”结构的许多“一起单击”函数,然后您必须模拟以进行测试。这是最好的国际海事组织 Go。
记住函数在 Go 中是一等的,你可以通过传递和返回函数而不是将它们绑定到接口来获得很大的灵活性。当你这样做时,你也可以将它们捆绑在一起作为一个结构。这可以提供很多功能,而这些功能恰好对测试很有用。例如看到,tls.Config
它允许您包含自己的GetCertificate
功能(以及其他功能)。但是该tls.Config
结构也具有合理的默认值,因此您不必每次都配置每个部分。通过传递一个特殊版本的tls.Config
,您可以在不“模拟”任何东西的情况下测试 TLS 功能。
这里一致的主题是使您的系统在其实现方面具有灵活性,并且作为一个很好的副作用,这使测试更容易。根据我的经验,这是一种比嘲笑更好的思考问题的方法。
你需要一个接口来模拟,因为模拟是你的第二个实现,所以如果你在单元测试中使用模拟,那么你的用例确实需要它。该接口充当占位符,以填充该功能的某些实现。您计划至少有两个这样的实现:一个模拟,一个生产。为了能够互换使用它们进行测试,您必须使用接口。
为了减少模拟更大接口的重复量,似乎可以使用类似vectra/mockery
的东西来生成模拟。然而,这不是消除对界面需求的解决方案,但我认为 Rob 在下面提到了一些好主意。