【发布时间】:2014-12-30 16:35:31
【问题描述】:
我正在尝试测试一些 C++ 遗留代码。特别是,我有一个类层次结构,比如A < B < C(即A 是B 的子类,B 是C 的子类),并且有一个对象的全局引用类型为C,用于整个系统代码(单例模式)。目标是用一些假对象替换 C 对象(实际上,C 用于访问数据库)。
我的第一个尝试是引入接口IA, IB, and IC(包含对应类功能的纯虚版本),让每个类实现自己的接口,并将全局C引用的类型改为IC .在我测试的设置函数中,我会用我自己的IC 实现替换全局引用的C 对象,让整个系统使用我的假实现。
但是,A, B 和 C 类都包含相当多的非虚拟函数。现在,如果我要让类从我的接口继承,我会将这些函数的语义从非虚拟更改为虚拟(Feathers 在“使用遗留代码高效工作”中讨论了这个问题,第 367 页)。换句话说:我必须检查对我的全局对象的每一次调用,并且我必须确保在我的更改之后,仍然调用相同的函数。对我来说,这听起来像是很多容易出错的工作。
我也想过让非虚函数“final”,即告诉编译器A, B和C的函数不能隐藏在子类中(这会让编译器告诉我所有潜在的危险B 和 C 的函数 - 如果函数没有隐藏在基类中,则根本不会发生上述效果),但 C++ 似乎不支持(我们还没有使用 C+ +11,但即使它的 final 关键字似乎也只适用于虚函数)。
为了让情况更加困难,A, B 和 C 类还包含公共属性、虚函数以及一些模板函数。
所以我的问题是:如何应对我上面描述的情况?是否有任何我错过的 C++ 功能,在我的场景中可以提供帮助?有什么设计模式吗?甚至任何重构工具?我的主要要求是更改必须尽可能安全,因为我想伪造的类对系统相当重要......我也很高兴有一个“丑陋”的解决方案,它可以让我进行测试到位(如果系统适当地覆盖了测试,则可以稍后对其进行重构)。
编辑:我搞砸了我的继承层次结构(它是颠倒的) - 现在已更正。
Edit2:我们最终得到如下结果:我们只将我们当前测试用例实际需要的函数设为虚拟。然后,我们检查了对这些方法的每次调用(这是可管理的)。这让我们可以使用 Google Mocks 来模拟我们的课程。有了越来越多的测试用例,希望我们的更改可以节省时间。请注意,在问我的问题时,我认为 Google Mocks 只能模拟纯接口; 不是这种情况,因此允许采用如上所述的增量方法。
【问题讨论】:
-
通常情况下,测试的第一个问题是全局对象,所以我建议在做任何其他事情之前摆脱它。
-
A和/或B是具体的类吗?如果是这样,他们需要吗? -
@Puppy:我们的系统有很多全局对象,我认为这是一个典型的先有鸡还是先有蛋的问题:为了安全地摆脱全局变量,我需要测试,并编写测试,我需要摆脱全局变量。因此,我现在尝试通过最少且尽可能安全的重构来进行测试,并且我计划稍后解决完整的全局问题。
-
@Mark B:它们是具体的类。很难看出它们是否确实需要具体,但我的猜测是它们不需要。为什么要问?
标签: c++ unit-testing inheritance virtual-functions