有没有办法打破单元测试的依赖性?

onu*_*lik 4 c++ polymorphism dependencies break

我的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();
}
Run Code Online (Sandbox Code Playgroud)

我想用以下解决方案打破这种依赖性(用于单元测试).这是代码

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

 class StubB : public FakeB 
 {   
   public:
      StubB();
      ~StubB();
      void m2(); 
 }
Run Code Online (Sandbox Code Playgroud)

但是当我实例化类A并使用以下代码调用其方法m1()时

A *ptr_a = new A(new StubB);
ptr_a->m1();
Run Code Online (Sandbox Code Playgroud)

方法m1()调用B的方法m2(),因为B的m2()不是虚拟的.B类是来自另一个模块的遗留代码,我不想改变它的代码,但我也不想改变A类的代码.

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

Doc*_*own 6

首先,由于delete ptr_b;在A new B()的构造函数中没有,因此在类A的析构函数中有一个错误的设计.这意味着每次创建A的实例时,您将B对象的所有权转移到A,留下您的delete使用A的人不知道内部的重复的潜在风险.

其次,如果你想给A一个"存根"(或"模拟"或"虚假")对象而不是"真实B",B并且FakeB需要一个包含B所有方法的公共接口,A需要它作为虚拟方法:

class FakeB : public InterfaceB 
Run Code Online (Sandbox Code Playgroud)

class B : public InterfaceB 
Run Code Online (Sandbox Code Playgroud)

所以A的所有成员函数都可以使用类型的参数InterfaceB *而不是B *.然后注入一个FakeB对象A显然很容易.

不幸的是,这意味着你必须改变B(至少,一点点).如果这不是一个选项,总有可能通过某个类包装B WrapperB (它与经典的Adapter模式大致相同):

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(); }
};
Run Code Online (Sandbox Code Playgroud)

WrapperB将只包含非常简单,直接的方法委托代码,您可以省略单元测试.而且你必须使用WrapperB而不是B当你将它与A一起使用时.但你得到的是一个完美的单元可测试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(); }

}
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用它:

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

FakeB fb;
A a_for_test(fb);
Run Code Online (Sandbox Code Playgroud)