2011-03-09 184 views
4

我的A类依赖于类B. 下面是代码有没有办法打破单元测试的依赖关系?

//declaration 
class A 
{ 
    public: 
    A(B *b); 
    ~A(); 
    void m1(); 
    private: 
    B *ptr_b; 
}; 

//implementation 
A::A(B *b) 
{ 
    ptr_b = b; 
} 

A::~A() 
{ 
    delete ptr_b; 
} 

void A::m1() 
{ 
    ptr_b->m2(); 
} 

我想打破这种依赖关系(单元测试)与下面的解决方案。 下面是代码

class FakeB : public B 
    {  
    public: 
     FakeB(); 
     ~FakeB(); 
     virtual void m2() = 0; 
    }; 

class StubB : public FakeB 
{ 
    public: 
     StubB(); 
     ~StubB(); 
     void m2(); 
} 

但是当我实例A类和用下面的代码调用它的方法M1()

A *ptr_a = new A(new StubB); 
ptr_a->m1(); 

方法M1()调用B的方法M2(),因为B的M2( )不是虚拟的。 B类是另一个模块的遗留代码,我不想更改其代码 ,但我也不想更改A类代码。

任何解决方案打破这种依赖?

回答

6

首先,它是具有A类的析构delete ptr_b;糟糕的设计,因为在A的构造这意味着每创建一个实例,你转化中的B对象的所有权时没有new B() A,给你一个使用A的人不知道内部结构的潜在风险,重复delete。第二,如果你想给A一个“存根”(或“模拟”,或“假”)对象而不是“真实的B”,BFakeB需要一个包含B中所有方法的公共接口,其中A需要如虚拟方法:

class FakeB : public InterfaceB 

class B : public InterfaceB 

所以A的所有成员函数可以使用InterfaceB *类型代替B *的参数。然后注入一个FakeB对象到A显然很容易。

不幸的是,这意味着你必须改变B(至少,一点点)。如果这是不是一种选择,总有一些类WrapperB (它主要是同样的想法,作为经典Adapter pattern)包B的可能性:

class WrapperB: public InterfaceB 
{ 
    B _b; 
public: 
    WrapperB(/* some parameters */) : _b(/* same parameters */){} 

    // Here you need to implement all methods of 
    // InterfaceB and delegate them to the original method calls 
    // of _b. You should give them the same name and signature as 
    // the corresponding (non-virtual) methods in B. 
    // For example, if there is a method m2 in B, 
    // there should be a pure virtual method m2 in InterfaceB, and 
    // an implementation here like this: 
    virtual void m2(){ _b.m2(); } 
}; 

WrapperB只会含有非常简单,直接的方法可以省略单元测试的授权代码。当你打算将它与A结合使用时,你必须使用WrapperB而不是B。但是你得到的是一个完美的单元可测试的class A

另一个(甚至更高)的变体正在建设中的WrapperB类的方式,你从外面注入到B对象的引用到它:

class WrapperB: public InterfaceB 
{ 
    B& _b; 
public: 
    WrapperB(B& b) :_b(b){} 

    // implement InterfaceB methods as above 
    virtual void m2(){ _b.m2(); } 

} 

您可以使用它,就像这样:

B b; 
A a(WrapperB(b)); 

FakeB fb; 
A a_for_test(fb); 
+1

你会详细介绍一下你的第二个解决方案吗? – metdos 2011-03-09 07:47:04

+0

谢谢你的糟糕设计。另外我需要你的第二个解决方案的进一步解释 – onurozcelik 2011-03-09 07:49:42

1

中断依赖关系的可能性是更改makefile中的include路径并包含您的B类版本。我不知道这是否适用于您的单元测试方案。

2

Merhaba厄尼尔

另一个想法是使用一些预处理器符号切换类之间的正常和单元测试模式的代码。例如:

文件A.hpp

#ifndef UNIT_TESTING 
# include "B.hpp" // contains "normal" class B 
#else 
# include "Testable_B.hpp" // contains "fake" class B, dedicated for unit testing. 
#endif 

UNIT_TESTING将是一个预处理器标志性建筑的单元测试时,你会只启用。

在情况下,如果文件中包含Testable_B.hpp类另一名不是“B”(例如,Testable_B),你还需要在类A的定义缺点是添加这些指令,如果有更多这样的修改是这会在类定义中造成混乱。

另一种方法是使用类型定义:

#ifndef UNIT_TESTING 
# include "B.hpp" // contains "normal" class B 
#else 
# include "Testable_B.hpp" // contains "fake" class B, dedicated for unit testing. 
    typedef Testable_B B; 
#endif 

我知道这是不是很优雅的解决方案,但也许你会发现它有用的,如果你不想修改类的代码。如果您绝对不想对源代码进行任何更改,那么stefaanv的解决方案可能就是要走的路。

+0

Merhaba tomac。我想你知道土耳其语:)我想我必须稍微改变一下A的代码。 – onurozcelik 2011-03-09 08:06:46

相关问题