2011-03-24 74 views
11

假设我有类在C++中模拟对象总是需要虚拟方法或模板吗?

class Inner { 
    public: 
    void doSomething(); 
}; 

class Outer { 
    public: 
    Outer(Inner *inner); // Dependency injection. 

    void callInner(); 
}; 

正确的单元测试说我应该有测试为Inner。然后,我应该对Outer进行测试,该测试不使用真实的Inner而是使用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; 

这避免了锅炉电镀和不必要的虚拟调度;但是现在我的整个代码库都在怪异的头文件中,这使得无法隐藏源实现(更不用说处理令人沮丧的模板编译错误和长的构建时间)。

这两种方法,虚拟和模板是进行适当单元测试的唯一方法吗?有更好的方法来做适当的单元测试吗?

通过适当的单元测试,我的意思是每个单元测试测试只能由单位推出的功能,但不是单位的依赖性也

回答

5

我不认为你必须在实践中嘲笑你的测试类的每个依赖。如果创建,使用或感知很复杂,那么是的。此外,如果它直接依赖于一些不需要的外部资源,如数据库,网络或文件系统。

但是,如果这些都不是问题,IMO可以直接使用它的实例。由于您已经对其进行了单元测试,因此您可以合理确信它按预期工作,并且不会影响更高级别的单元测试。

我个人更喜欢工作单元测试和简单,干净,可维护的设计,以坚持由单元测试纯粹主义者设定的一些理想。

每个单元测试只测试该单元引入的功能,但不测试单元的相关性。

使用功能测试功能是两个完全不同的事情。

+0

我个人更喜欢工作单元测试和简单,干净,可维护的设计,以坚持单元测试纯粹主义者设定的一些理想。 我喜欢这个回应---保持事物的实用性。 – kirakun 2011-03-24 22:46:38

2

我也认为直接在Inner上使用实例是可以的。 我的问题是嘲笑不属于我的代码(通过静态库或DLL,有时是第三方提供)的外部对象。 我倾向于用相同的类名重写模拟的DLL或库,然后进行不同的链接以进行测试。修改外部依赖项的头文件以添加“虚拟”对我来说似乎是不可接受的。 有没有人有更好的解决方案?