.NETの場合、インターフェースを「フィード」するのに十分な素晴らしいRhino.Mocksライブラリがあり、ユニットテストでインターフェースメソッドの動作を直接プログラムする機会が得られます。 C ++の場合、実行時にコードをビルドできるすばらしいリフレクションがないため、すべてがはるかに複雑です。 また、スタブオブジェクトを手動で記述する必要があります。 また、インターフェイスが変更された場合は、アプリケーションのすべてのクラスを更新するだけでなく、テストで使用されるインターフェイスを実装するスタブの「1回限りの」クラスのセット全体を更新する必要があります。
解決策
私はとても怠け者です。Pythonやperlで何らかの自動生成プログラムができるコードを書くのは好きではありません。 また、この概念も気に入っています。コードには「冗長な」#defineディレクティブをできるだけ含めないでください。また、可能であれば、すべてのテストスイートがクラスにパックされるようにします。 それが、C ++のすべての単体テストサポートライブラリの中で、すべてのテストスイートに目次ジェネレーターの概念を使用するCxxTestを好む理由です。
モックオブジェクトにも同じことが当てはまります。テストで使用されるインターフェイスのスタブを実装する、あらゆる種類のワンタイムクラスを書き換えるのは好きではありません。 そして、結局のところ、Rhino.Mocks for .Netと同じ手法をコンパイルレベルでのみ適用できるのであれば、なぜそれらを書き換えるのでしょうか。
すぐに言ってやった! CxxMockの紹介( GitHubのミラー)。
備考1:ライブラリはかなり前に作成されたもので、私のプロジェクトで非常にうまく使用されています。
注2:他のソリューション、たとえば同じgooglemockが存在しますが、イデオロギー的にCxxTestと互換性がありません。
注3: CxxMockは、可能であれば、 Rhino.Mocksと同じシグニチャーシステムを使用して、C#およびC ++でプロジェクトを作成している場合、再トレーニングに問題はありません。
クイックスタート
1.テストに必要なすべてのインターフェイスの実装を含むヘッダーファイルの自動生成のステップを追加します。
python cxxmockgen.py <IMyCoolInterface.h> <header.h> <header.h>.... >generated_mocks.h
2.テストで使用し、C#のRhino.Mocksで実行します
#include "generated_mocks.h" // . class TestMyMockObjects : public CxxTest::TestSuite { ... ... void testQuickStart() { // mock- CxxMock::Repository mocks; // IMyCoolInterface* mock = mocks.create<IMyCoolInterface>(); // : // IMyCoolInterface::method() 10 5 TS_EXPECT_CALL( mock->method(10) ).returns( 5 ); // TS_EXPECT_CALL_VOID( mock->voidMethod() ); // CxxMock // . mocks.replay(); // // - IMyCoolInterface::method() 10 . // , - TS_ASSERT_EQUALS( 5, mock->method(10) ); // - IMyCoolInterface::voidMethod() // , - mock->voidMethod(); // // . mocks.verify(); } }
CxxTest互換のシグネチャを持つ2つのマクロがここに適用されます
- TS_EXPECT_CALL-値を返すメソッドを待機している呼び出しをプログラミングする
- TS_EXPECT_CALL_VOID-値を返さないvoidメソッドを待つ呼び出しのプログラミング
他に何ができますか?
CxxMockはRhino.Mocksのすべての機能をサポートしているわけではありませんが、最も必要な機能はサポートしています。 ここでは、それらの使用方法を示します。
戻り値の設定:
TS_EXPECT_CALL( object->method() ) .returns( retvalue );
コール数のチェックを外します;デフォルトでは、1つのコールのみがプログラムされます。 たとえば、標準的な変更通知の場合など、この呼び出しに関心がない場合に役立ちます。
TS_EXPECT_CALL( object->method() ) .repeat().any();
1つのコールのみが予期されることを明示的に示します。 これがデフォルトの動作です。 コールウェイティングが設定されている場合、CxxMockはコールが1つしかなかったことを確認します。
TS_EXPECT_CALL( object->method() ) .repeat().once();
待機するメソッド呼び出しの数を明示的に示します。 呼び出しが少ないか多い場合、検証は失敗します
TS_EXPECT_CALL( object->method() ) .repeat().times(5);
引数を確認しないでください。 完成したシステムをテストでカバーし、その仕組みを理解したくない場合に適用すると便利です。 ただ電話があったか、このチェックをスキップしたいことが重要です...
TS_EXPECT_CALL( object->method() ) .ignoreArguments();
引数と呼び出し回数に関係なく、常に数値10を返すメソッドの期待値をプログラミングする一般的なケースの例。
TS_EXPECT_CALL( object->method("value1", 5) ) .ignoreArguments() .returns( 10 ) .repeat().any();
CxxTestでできないことは何ですか?
最もスマートなプログラムでさえ、常に制限があります。 CxxMockには、コードに基づいた独自のコードジェネレーターがあるため、疑問が生じます。これは、特にコードで機能しますか?
回答:多分そうでしょう、そうでないかもしれません。 CxxMockは、ネストされた名前空間を含む名前空間をサポートしますが、メソッドシグネチャを非常に要求します。
たとえば、インターフェイスメソッドのこのような署名はサポートされていません。
class NotSupportedInterface { public: virtual void canNotSetPointer2(int *arg) = 0; virtual void canNotSetPointer3(int&arg) = 0; virtual void canNotSetReference2(int &arg) = 0; virtual void canNotSetReference3(int&arg) = 0; virtual ~NotSupportedInterface(){} };
しかし、そのようなものはサポートされています
class Interface { public: virtual void setValue( Type arg ) = 0; virtual Type getValue() = 0; virtual Type& canGetReference() = 0; virtual Type* canGetPointer() = 0; virtual void canSetPointer( Type* arg ) = 0; virtual void canSetReference(Type& arg) = 0; virtual void canParseCompressed(Type arg1, Type arg2) = 0; virtual Type canParseReference(const Type& arg) = 0; virtual void canSetConstParam(const Type* arg) = 0; virtual void voidMethod() = 0; virtual ~Interface(){} };
引数の基本的なルールは、修飾子+タイプ+ スペース +名前です。 この場合、 タイプは1ワードで指定する必要があります。
また、CxxMockは次のことができません。
- CxxMockは、抽象クラスおよびテンプレートでは機能しません。
- CxxMockには、条件によって引数をチェックするためのメソッドがありません;引数は常に等しいかどうかチェックされます。
- CxxMockは、メソッド呼び出しを待機する任意の順序をサポートしていません;順序はハードコードされています。
- CxxMockは複雑な型をサポートしません。たとえば、Type *&、Type <A、B>-typedefを使用して通常の名前を取得すると、デバッグが容易になります。
- CxxMockは、呼び出し(.do()メソッド)のアクションの設定をサポートしていません。将来表示される可能性があり、モックオブジェクトの手動実装を使用するようになりました。
おわりに
アジャイルを信じ、C ++プロジェクトの単体テストにCxxTestライブラリを使用するが、インターフェイスを使用してオブジェクトをチェックするために手動で作成されたスタブオブジェクトのサポートに時間を費やす場合、CxxMockはタスクを大幅に簡素化できます。
#defineディレクティブの使用は最小限であるため、IDEツールを使用すると、「1回限りの」オブジェクトを維持する追加コストなしに、このインターフェイスメソッドが使用されているすべての場所を常に見つけることができます。