5

我正在开发使用boost::asio. 为了测试它,我需要从这个库中模拟一组类。我正在使用 Google Mock,它允许模拟虚拟方法。通常(且乏味)的过程是为我需要使用的每个类编写一个接口。

另一方面,Google Mock Cookbook 描述了模拟非虚拟方法的另一种方法:使用模板。我的问题是我可能需要同时模拟几个类(所以直接使用模板是行不通的)。于是我想:为什么不用两级模板呢?我想出了以下解决方案:

// Classes to be mocked.

class RealA
{
public:
    void a() { cout << "RealA::a()" << endl; };
};

class RealB
{
public:
    void b() { cout << "RealB::b()" << endl; };
};

// Mock classes.

class MockA
{
public:
    void a() { cout << "MockA::a()" << endl; };
};

class MockB
{
public:
    void b() { cout << "MockB::b()" << endl; };
};

template<class ABFactory>
class Program
{
public:
    void setFactory(ABFactory* factory) { factory = factory; }
    void useA() { typename ABFactory::A* a = factory->createA(); a->a(); delete a; }
    void useB() { typename ABFactory::B b; b.b(); }

private:
    ABFactory* factory;
};


template<class ParamA, class ParamB>
class TABFactory
{
public:
    typedef ParamA A;
    typedef ParamB B;
    A* createA() { return new A; };
    B* createB() { return new B; };
};

typedef TABFactory<RealA, RealB> RealABFactory;
typedef TABFactory<MockA, MockB> MockABFactory;

然后,正常的用法是:

Program<RealABFactory> p;
p.useA();
p.useB();

虽然测试将是:

Program<MockABFactory> t;
t.useA();
t.useB();

当被模拟的类具有带有复杂参数的方法(例如来自同一库的其他类可能未被模拟)时,这开始变得复杂。总而言之,它似乎没有规模化。对这个解决方案有什么想法,或者对解决问题的其他方法有什么建议吗?

4

1 回答 1

4

这是python cog的完美用例

另请参阅此答案

我使用 cog 为事件列表生成处理程序,处理程序代码非常通用,我不需要做特殊情况,但我仍然必须编写所有函数,所以我所做的就是将事件保存在.py 文件中的列表和在 python 函数中生成处理程序样板的代码。所以我能够忠实于DRY 原则

显然,您必须将 cog 添加到您的 makefile 的预构建中才能使用您的工具链

编辑作为将样板添加到您的类所需的代码生成设计的示例,我会执行以下操作:

我的代码生成.py

import cog
ClassesToMock = [ [ 'IfaceA' , 'classA' , 'mockA' , ['void doSomething(int foo)' , 'int getSomething()'] 
                 , [ 'IfaceB', 'classB' , 'mockB' , ['static classA& getInstance()'] ]

def addInterfaces( myStructure ):
   for classItem in myStructure:
      cog.outl('class %s { ' % classItem[0] )
      for methodDecl in classItem[3]:
         cog.outl(' virtual %s = 0;' %methodDecl )
      cog.outl(' } ')

#implement your real classes normally

def addMocks( myStructure ):
   for classItem in myStructure:
      cog.outl('class %s : public %s { ' % classItem[2] % classItem[0] )
      for methodDecl in classItem[3]:
         cog.outl(' %s {' %methodDecl )
         cog.outl(' MOCK_STUFF_MACRO ')
         cog.outl(' } ')
      cog.outl(' } ')

然后在你的标题中:

IfaceA.h

/*[[[cog
import cog
import myCodeGeneration

myCodeGeneration.addInterfaces( [ [ 'IfaceA' , 'classA' , 'mockA' , ['void doSomething(int foo)' , 'int getSomething()'] ] )
]]]*/

//your code will be generated here

//[[[end]]]

模拟A.h

/*[[[cog
import cog
import myCodeGeneration

myCodeGeneration.addMocks( [ [ 'IfaceA' , 'classA' , 'mockA' , ['void doSomething(int foo)' , 'int getSomething()'] ] )
]]]*/

//your code will be generated here

//[[[end]]]

此外,如果考虑将 python 添加到您的 c++ 源代码中,问题是“污染”它或“美化”它,这在很大程度上取决于品味和风格。我认为 cog 提供了对 c++ 中缺乏的模板样式元编程的补充,为程序员提供了提供代码整洁性和可读性的工具。但我不希望每个人都同意

对我来说,这种方法背后的整个架构设计原则是不要重复自己。当我们必须在几个地方手动对方法进行编码时,就会发生错误。让计算机自动化它可以自动化的东西,并对不能只进行一次的东西进行编码。作为一个副作用,它会让你的编码更有趣,无论是编写它还是以后阅读它。

希望这可以帮助

于 2012-04-21T15:52:33.743 回答