2016-06-19 2 views
2

とよく似ていますが、これはHow to use gmock to test that a class calls it's base class' methodsと非常に密接に関連していますが、私の例ではこれを動作させるのに苦労しています。GMock check base class関数の使用方法は

私は、派生クラスを持っているし、新しい機能は、このようなときということで、他のTDDに基づいて

...私は、基本クラスを持っているように、新しい機能をテストドライブする

class SimpleObject 
{ 
public: 
    explicit SimpleObject() {} 

    virtual void moveX(int dX) 
    { 
     // Do important stuff like updating position, bounding box etc. 
    } 

    // ... 
}; 
をGTESTとGMockを使用しています派生したオブジェクトに対してmoveXを呼び出すと、何か具体的なことが実行されますが、SimpleObject :: moveXでは重要なことも行う必要があります。

私はすでにSimpleObject :: moveX関数に関連するテスト駆動ユニットテストを持っていますので、派生クラスに対してそれらを繰り返す必要はありません。私が知っている限り、SimpleObject :: moveXが呼び出され、すべてがうんざりです。

とにかく、上記のリンクとTDDに基づいて、私は次のようになりました。

派生クラス:

class ComplexObject : public SimpleObject 
    { 
    public: 
     virtual void moveX(int dX) 
     { 
      // Do something specific 
     } 
    }; 

'テスト可能な' クラス:

class TestableComplexObject : public ComplexObject 
{ 
public: 
    MOCK_METHOD1(moveX, void(int dX)); 

    void doMoveX(int dX) 
    { 
     SimpleObject::moveX(dX); 
    } 
}; 

テスト:私は私のテストを実行する場合

TEST_F(ATestableComplexObject, CallsBaseClassMoveXWhenMoveXIsCalled) 
{ 
    int dX(8); 
    TestableComplexObject obj; 

    EXPECT_CALL(obj, moveX(dX)) 
       .Times(1) 
       .WillRepeatedly(testing::Invoke(&obj, &TestableComplexObject::doMoveX)); 

    obj.moveX(dX); 
} 

その後、すべてが合格します。 ComplexObject :: moveXは何もしませんので、これは正しくありません。

また、私がdoMoveXに何を置いても(これは私が期待していたと思っていたものですが)、テストは引き続き行われます。

私は明らかにここに何か簡単なものがないので、どのようなアイデアですか?

答えて

0

ここでの主な問題は、メソッドTestableComplexObject::doMoveXでは、メソッドSimpleObject::moveXを呼び出していることです。これがすべての理由です。クラスComplexObjectに属するmoveXメソッドを呼び出すことになっています。したがって、方法をdoMoveXに変更すると、

void doMoveX(int dX) 
{ 
    ComplexObject::moveX(dX); 
} 

が解決されます。

投稿したコードにもう1つ問題があります。試験体の最後の文は次のようになります。

obj.moveX(dX); 

が、私はこれがちょうど質問を書いている間に行われた誤りであると思いますか?

希望すると便利です。

+0

私はdoMoveXに何を置いても、テストは合格となります。さらに、ComplexObject :: doMoveXを呼び出すと、これは確実に派生した関数を呼び出すのではなく、確実に呼び出す必要のある基底関数を呼び出すでしょうか? –

+0

はい、最後のステートメントはタイプミスでした。私は元の投稿を編集します –

1

ComplexObjectをデザインして、SimpleObject::moveXが呼び出されたかどうかを確認する必要があります。それを行うための一つの方法:ちょうどこの基本機能をモックあなたTestableクラスに続いて

class ComplexObject : public SimpleObject 
{ 
public: 
    virtual void moveX(int dX) 
    { 
     // Call base function 
     baseMoveX(dx); 
     // Do something specific 
    } 
protected: 
    virtual void baseMoveX(int dX) 
    { 
     SimpleObject::moveX(dx); 
    } 
}; 

:嘲笑することができますいくつかの他の機能と、このベースコールをカプセル化

class TestableComplexObject : public ComplexObject 
{ 
public: 
    MOCK_METHOD1(baseMoveX, void(int dX)); 
}; 

あなただけmoveX理由を模擬することはできませんそのような文脈で、基本と派生とを区別する方法はありません。

そう - あなたのテストは次のようになります。それはコメントで発見されたよう

TEST_F(ATestableComplexObject, CallsBaseClassMoveXWhenMoveXIsCalled) 
{ 
    int dX(8); 
    TestableComplexObject obj; 

    EXPECT_CALL(obj, baseMoveX(dX)) 
       .WillOnce(testing::Invoke([&obj] (auto dx) {obj.SimpleObject::moveX(dx); })); 

    obj.moveX(); 
} 

[UPDATE]

- ComplexObjectを確保するためにどのような問題が依然として存在します。 baseMoveX()はSimpleObject :: moveXを呼び出します。

解決方法は、ComplexObjectとSimpleObjectの間にもう1つのクラスを配置することです。本当にこれを確実にテストして

template <typename BaseObject> 
class IntermediateObject : public BaseObject 
{ 
public: 
    virtual void baseMoveX(int dX) 
    { 
     BaseObject::moveX(dx); 
    } 
}; 

起こります。そして、

class TestableBaseMock 
{ 
public: 
    MOCK_METHOD1(moveX, void(int dX)); 
}; 

TEST(IntermediateObject Test, shallCallBaseMoveX) 
{ 
    const int dX = 8; 
    IntermediateObject<TestableBaseMock> objectUnderTest; 
    TestableBaseMock& baseMock = objectUnderTest; 

    EXPECT_CALL(baseMock, moveX(dX)); 

    objectUnderTest.baseMoveX(dx); 
} 

- 単純および複合クラス間にそれを置く: - 私は強調したい終わり

class ComplexObject : public IntermediateObject<SimpleObject> 
{ 
public: 
    virtual void moveX(int dX) 
    { 
     // Call base function 
     baseMoveX(dx); 
     // Do something specific 
    } 
}; 

を元のデザインを変更すること - 継承の代わりにアグリゲーションを使用すること(つまり、デコレータデザインパターン)が最良の方法です。まず、私の答えで見ることができるように、継承を維持しようとすると、テストしたい場合、デザインが悪化します。デコレータの種類の2番目のテストは、答えの1つに示されているようにはるかに簡単です...

+0

それは意味があり、私はそれがどのように動作するか見ることができます。しかし、基本クラスの関数をもっとたくさん派生させるとどうなりますか?これらすべての機能を効果的に複製することは、依然として受け入れられる良い設計ですか? –

+0

変更を実装すると、obj.moveX(dX)行でseg faultが発生します。 –

+0

私はこの例を試してみて、ComplexObject :: baseMoveXとComplexObject :: moveXを空にしました。最初のテストの実行は失敗したので、渡されたComplexObject :: moveXの中にbaseMoveXコールを入れました。私のComplexObject :: baseMoveXは何もしないので、渡すべきではありません。これは私が以前にあったのと同じ状況です –

1

コメントをいただきありがとうございます、私は私が欲しいものをテストすることができます。

まず、SimpleObjectためのインタフェースを作成します。

class ISimpleObject 
{ 
public: 
    virtual void moveX(int dX) = 0; 
}; 

マイSimpleObjectクラスは、この実装しています

SimpleObjectから継承し、それはインタフェースから継承し、(SimpleObjectを所有する代わりにComplexObjectの
class SimpleObject : public ISimpleObject 
{ 
public: 
    explicit SimpleObject() {} 

    virtual void moveX(int dX) 
    { 
     (void) dX; 
     // Do important stuff like updating position, bounding box etc. 
    } 
}; 

それは 'a'ではなく 'a'を持っています)。コンストラクタは、SimpleObjectを渡すことを保証します。この注入によって、簡単に嘲笑することができます。

class ComplexObject : public ISimpleObject 
{ 
public: 
    ComplexObject(SimpleObject *obj) 
    { 
     _obj = obj; 
    } 

    virtual void moveX(int dX) 
    { 
     _obj->moveX(dX); 
    } 

private: 
    SimpleObject *_obj; 
}; 

は今、私は単にSimpleObject

から
class SimpleObjectMock : public SimpleObject 
{ 
public: 
     MOCK_METHOD1(moveX, void(int dX)); 
     // Do important stuff like updating position, bounding box etc. 
}; 

に私は興味の呼び出しを模擬試験は、行動が期待されている通りすぎ

TEST_F(AComplexObject, CallsBaseClassMoveXWhenMoveX) 
{ 
    int dX(8); 

    SimpleObjectMock mock; 
    ComplexObject obj(&mock); 

    EXPECT_CALL(mock, moveX(dX)).Times(1); 

    obj.moveX(dX); 
} 

簡略化されています。 ComplexObject :: moveX関数が空の場合(開始時に実行されるため)、テストは失敗します。これは、SimpleObject :: moveXを呼び出すときにのみ渡されます。

関連する問題