5

を使用するコードを開発していますboost::asio。テストするには、このライブラリから一連のクラスをモックする必要があります。仮想メソッドをモックできる Google Mock を使用しています。通常の (そして面倒な) プロセスは、使用する必要があるクラスごとにインターフェイスを作成することです。

一方、Google Mock Cookbook では、非仮想メソッドをモックする場合の代替手段として、テンプレートの使用について説明しています。私の場合の問題は、同時に複数のクラスをモックする必要がある可能性があることです (そのため、テンプレートを直接使用しても機能しません)。だから私は考えました: 2 レベルのテンプレートを使用しないのはなぜですか? 私は次の解決策を思いつきました:

// 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

これは、 PythonCogの完璧なユースケースです。

この回答も参照してください。

cogを使用してイベントのリストのハンドラーを生成しました。ハンドラーコードは非常に一般的で、特別な場合を行う必要はありませんが、それでもすべての関数を作成する必要があるため、イベントを.pyファイルにリストし、Python関数でハンドラーのボイラープレートを生成するコードを記述します。だから私はDRYの原則に忠実であることができます

明らかに、ツールチェーンを操作するには、makefileのプレビルドに歯車を追加する必要があります

クラスに定型文を追加するために必要なコード生成設計の例として編集します。次のようにします。

myCodeGeneration.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]]]

mockA.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 ++ソースに追加することを検討している場合の問題は、Pythonを「汚染」するか、「美化」することであり、主に好みとスタイルの問題です。cogは、c ++に欠けているテンプレートスタイルのメタプログラミングを補完し、コードの整理と読みやすさを提供するツールをプログラマーに提供すると思います。しかし、私は誰もが同意することを期待していません

私にとって、このアプローチの背後にある建築設計の原則全体は、Do n'tRepeatYourselfです。複数の場所でメソッドを手動でエンコードする必要がある場合、エラーが発生します。コンピューターに自動化可能なものを自動化し、一度だけでは不可能なものをエンコードさせます。副作用として、それはそれを書くことと後でそれを読むことの両方のためにあなたのコーディングをより楽しくするでしょう。

お役に立てれば

于 2012-04-21T15:52:33.743 に答える