重构Singletons / Globals以使用依赖注入进行单元测试

时间:2014-01-10 17:07:37

标签: c++ unit-testing design-patterns dependency-injection tdd

我正在开发一个大量使用Singleton模式的代码库,以及一些Globals。我刚刚开始尝试编写一些单元测试,但Singletons和Globals给我带来了很多问题,在阅读之后,依赖注入看起来就好了。

为实现这一改变而进行的重构任务非常艰巨,我正试图找出最佳方法。据我所知,基本的想法是采取这样的方式:

foo()
{
    GraphicsCache::Instance()->GetMyImage();
    // do stuff
}

并把它变成这样的东西:

foo(GraphicsCache *Cache)
{
    Cache->GetMyImage();
    // do stuff
}

这样我可以制作这些对象的模拟并在我的测试中使用模拟。但是有很多这些类型的对象(事件记录器,网络对象,其他缓存等),这几乎意味着我最终会在整个地方传递大量对象,并最终会在整个地方添加了大量代码。我有正确的想法吗?有更好的方法吗?

我的一个想法是让一个对象成为所有这些当前全局对象的容器,而我所要做的就是传递一个引用,但这并不像大多数地方那样合适不需要访问每个单一的全局对象,只需要一个子集。

2 个答案:

答案 0 :(得分:3)

你应该按照你的建议去做,传递物体是正确的方法。 您可能也想使用工厂。是的,你最终会添加很多代码,但这是值得的。

不要创建一个“包”对象来传递,从它看到它创建与全局变量相同的问题,你不能告诉你的对象真正依赖什么,因为一切都被传递他们。

如果您真的没有那么多时间,或者由于其他原因(例如您的经理对此不满意)可能无法工作,您还可以创建专用工厂并更改工厂创建的对象测试。

您可以详细了解它,它被称为Interface Segregation Principle

简单地说,该原则规定一个类不应该依赖于它不使用的函数。

答案 1 :(得分:1)

我是从c#的角度回答这个问题,但它同样适用于C ++。对象肯定比单例更好,但对于真正的横切关注点(即记录等......),您可能需要考虑将这些单例修改为AmbientContext模式。它基本上是类固醇的单例,但你在应用程序/测试启动时将它实例化为你选择的接口实现。这将使记录仪完全可测试,同时不会导致您必须将它们注入所有内容。

环境背景也可以作为一个临时的中间地带,允许进行测试,以防止必须在旅途中重构所有这些单身人士。