假设我有课
class Inner {
public:
void doSomething();
};
class Outer {
public:
Outer(Inner *inner); // Dependency injection.
void callInner();
};
适当的单元测试表明我应该对Inner
. 然后,我应该对此进行测试,而Outer
不是使用真实Inner
的,而是使用 a MockInner
,这样我就可以对添加的功能执行单元测试,Outer
而不是完整的堆栈Outer
/ Inner
。
为此,Googletest似乎建议Inner
变成这样的纯抽象类(接口):
// Introduced merely for the sake of unit-testing.
struct InnerInterface {
void doSomething() = 0;
};
// Used in production.
class Inner : public InnerInterface {
public:
/* override */ void doSomething();
};
// Used in unit-tests.
class MockInner : public InnerInterface {
public:
/* override */ void doSomething();
};
class Outer {
public:
Outer(Inner *inner); // Dependency injection.
void callInner();
};
所以,在生产代码中,我会使用Outer(new Inner)
; 在测试时,Outer(new MockInner)
.
好的。理论上看起来不错,但是当我开始在整个代码中使用这个想法时,我发现自己为每个该死的类创建了一个纯抽象类。即使您可以忽略由于不必要的虚拟调度而导致的轻微运行时性能下降,这是很多样板类型。
另一种方法是使用模板,如下所示:
class Inner {
public:
void doSomething();
};
class MockInner {
public:
void doSomething();
};
template<class I>
class Outer {
public:
Outer(I *inner);
void callInner();
};
// In production, use
Outer<Inner> obj;
// In test, use
Outer<MockInner> test_obj;
这避免了样板化和不必要的虚拟调度;但现在我的整个代码库都在该死的头文件中,这使得隐藏源代码实现变得不可能(更不用说处理令人沮丧的模板编译错误和漫长的构建时间了)。
虚拟和模板这两种方法是进行正确单元测试的唯一方法吗?有没有更好的方法来进行适当的单元测试?
通过适当的单元测试,我的意思是每个单元测试只测试该单元引入的功能,而不是单元的依赖项。