重构单例/全局以使用依赖注入进行单元测试

问题描述 投票:0回答:2

我正在开发一个大型代码库,该代码库广泛使用了单例模式以及一些全局变量。我刚刚开始尝试编写一些单元测试,但是单例和全局给我带来了很多问题,在阅读完之后,依赖注入似乎是可行的方法。

进行此更改所需的重构任务非常艰巨,我正在努力找出最佳方法。据我了解,基本想法是采取这样的事情:

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

然后把它变成这样:

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

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

我的一个想法是拥有一个对象,它是所有这些当前全局对象的容器,而我所要做的就是传递一个引用,但这感觉不对,因为大多数地方都不需要访问每个全局对象,只是一个子集。

c++ unit-testing design-patterns dependency-injection tdd
2个回答
4
投票

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

不要创建要传递的“包”对象,从我所见它会产生与全局变量完全相同的问题,您无法判断您的对象真正依赖什么,因为所有内容都被传递给它们.

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

您可以阅读更多相关内容,它被称为接口隔离原则

简单地说,该原则指出 类不应依赖于它不使用的函数。


1
投票

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

环境上下文也可以作为临时的中间立场,以允许测试避免在运行中重构所有这些单例。

© www.soinside.com 2019 - 2024. All rights reserved.