2016-05-04 9 views
1

私は、何も制御していない外部クラスのサブクラスであるクラスを持っています。外部クラスはシステムリソースに依存します。たとえば、外部システムに依存する外部基底クラスから派生したクラスをテストする方法

class MyClass : public ExternalBase // This class is from external framework and framework requires it to derive from this class. 
{ 
    int doSomePrivateThing(int); 
public: 

    virtual int DoSomething(int); 
    virtual ~MyClass(); 
} 

int MyClass::doSomePrivateThing(int) 
{ 
    // do some private task 
} 

int MyClass::DoSomething(int n) 
{ 
    // Do MyClass Specific task 
    int k = doSomePrivateThing(n); 
    return ExternalBase::DoSomething(k); // This function depends on external system resources. 
             // Probably try to communicate with remote server 
             // or attempt access Storage or Display device etc. 
} 

MyClass::~MyClass() 
{} 

MyClassの依存関係を解除し、単体テストをMyClass :: DoSomething()に書き込む方法を教えてください。継承の代わりにコンポジションを使用することは、フレームワークがこの基本クラスからクラスを派生させる必要があるため、選択肢ではありません。

私はC++とGoogleTest/Mockを使用しています。しかし、どのような一般化された解決策が評価されます。

ありがとうございます。

+1

あなたはユニットテスト、これをすることはできません。物語の終わり。 – SergeyA

+0

@SergeyA私はそのようにあきらめることはできません。依存関係を解消するためにマクロやテンプレートトリックを行うことはできませんか?アイデアはありますが、それが良いかどうかはわかりません。私はそれを答えとして掲示します。しかし、誰か他の人が良いアイデアを持っているなら、私は他人を待っています。 – army007

+0

私の経験はCppUTestであるため、用語がオフになっている可能性があります。しかし、予想される最高のことは、MyClass :: DoSomething関数が期待通りに外部呼び出しまで機能していることを確認することです。その時点で、期待通りのものを渡すことをテストできます。 CppUTestでは、これは嘲笑によって達成され、私はGoogleTestに相当するものがわからない。 – Aumnayan

答えて

2

2つの方法があります。私はそれらを「もう少し正しい」方法と「非常に醜い」方法と呼びます。


「より正しい」方法:

嘲笑部分的でできるよりも、いくつかの追加の層で外部クラスの機能を囲みます。このようにUTで

class MyClass : public ExternalBase // This class is from external framework and framework requires it to derive from this class. 
{ 
    int doSomePrivateThing(int); 
public: 
    virtual void BaseDoSomething(int) { return ExternalBase::DoSomething(v); } 
    virtual int DoSomething(int v); 
    virtual ~MyClass(); 
}; 

int MyClass::DoSomething(int n) 
{ 
    // Do MyClass Specific task 
    int k = doSomePrivateThing(n); 
    return BaseDoSomething(k); 
} 

と部分モック:

class TestableMyClass : public MyClass 
{ 
public: 
    using MyClass::MyClass; 
    MOCK_METHOD1(BaseDoSomething, int(int)); 
}; 

TEST(A,A) 
{ 
    TestableMyClass objectUnderTest; 
    EXPECT_CALL(objectUnderTest, BaseDoSomething(112)); 

    objectUnderTest.DoSomething(112); 
} 

あなたのテストでも、真の基底クラスのメソッドを呼び出す必要が

- EXPECT_CALLで WillOnce(Invoke...)を使用しています。


「非常に醜い」方法:

はExternalBaseの独自のunittestの実装を提供し、あなたのテストにリンクします。 ExternalBaseのこの「UnitTest」impolementationは、いくつかのグローバルなMockオブジェクトに基づいていなければなりません。

ExternalBaseMock.hpp:

class ExternalBaseMock 
{ 
public: 
    MOCK_METHOD1(DoSomething, int(int)); 
}; 
extern ExternalBaseMock externalBaseMock; 

ExternalBaseMock.cpp:

ExternalBaseMock externalBaseMock; 

int ExternalBase::DoSomething(int n) 
{ 
    return externalBaseMock.DoSomething(n); 
} 

次に、あなたのテスト:

#include "ExternalBaseMock.hpp" 

TEST(A,A) 
{ 
    MyClass objectUnderTest; 
    EXPECT_CALL(externalBaseMock, DoSomething(112)); 

    objectUnderTest.DoSomething(112); 
} 
+0

ありがとうございます。両方の方法は非常に効果的で、私の問題を解決しました。私は最も「醜い方法」が好きです。私は条件付きコンパイルを使用し、 'ExternalBase'クラスをこの偽のクラスに置き換えることができるからです。 「より正確な方法」では、(テストしたい)すべての基本クラスメソッドは、派生クラスに対応するメソッドを持たなければなりません。 @PiotrNycz「醜い方法」と呼ぶ理由はありますか?この方法で問題が発生する可能性はありますか? – army007

+0

問題はそのスレッド不親切であり、そのような "醜い嘲笑"クラスの事実上ただ一つのオブジェクトを持つことができます。 UTのほとんどの場合、大きな問題ではありません。もっと重要なことは、bla bla blaを嘲笑する哲学に対してちょっとしたことです...それがあなたのニーズに最も適している場合は、それを使用してください... – PiotrNycz

+0

BTW両方の問題には回避策がありますが、おそらく大したことはありません – PiotrNycz

関連する問題