9

私はPersonのような単純でほぼ価値のあるクラスを持っています:

class Person
{
public:
    Person(ThirdPartyClass *object);
    virtual ~Person(void);

    virtual std::string GetFullName() const;
    virtual int GetAge() const;
    virtual int GetNumberOfDaysTillBirthday() const;
};

サードパーティのライブラリを使用していますが、それを破棄するには、(サードパーティのライブラリの一部である)というThirdPartyClassグローバル/静的関数を呼び出す必要があります。DestroyこのDestroy関数は、Personデストラクタで呼び出されます。

現在、Personクラスの単体テストを試みていますが、メソッドをモック/スタブするDestroy方法が必要です。静的Destroy関数の周りにラッパークラスを記述し、依存性注入を使用してこのラッパーをPersonクラスに注入できると思いますが、この単純なクラスでこの1つの関数を呼び出すだけではやり過ぎのようです。これを行うための簡単で簡単な方法は何ですか?それとも、依存性注入は本当にこれを行うための最良の方法ですか?

アップデート

最終的には、サードパーティライブラリのすべてのグローバル関数をラップするクラスを作成し、依存性注入を使用してこのクラスをpersonクラスのコンストラクターに渡すことにしました。このようにして、Destroyメソッドをスタブすることができます。個人クラスは単一の関数のみを使用しますが、ライブラリの他の関数はコードの他のポイントで呼び出され、それらをテストする必要があるため、同じ問題に直面します。

メインのアプリコードでこのラッパークラスの単一インスタンスを作成し、必要に応じて挿入します。わかりやすいと思うので、このルートを選びました。Billy ONealのソリューションが好きで、それが私の質問に答えると思いますが、コードを数か月残して戻ってきた場合、依存性注入と比較して、何が起こっているのかを理解するのに時間がかかることに気付きました。「明示的は暗黙的よりも優れている」というPythonの格言の禅を思い出します。依存性注入により、何が起こっているのかがもう少し明確になると思います。

4

4 に答える 4

10

リンクシームを作成します。destroy宣言をヘッダーに入れてから、2つの実装ファイルを作成します。

// Destroy.cpp
void Destroy()
{
    //Code that really does destruction
}

そしてテストのために:

// DestroyFake.cpp
void Destroy()
{
    //Code that does fake destruction
}

次に、最初のファイルをメインバイナリにリンクし、2番目のファイルをテストバイナリにリンクします。

これを超えて、あなたが求めることは不可能です。リンカは、グローバル関数と静的メソッドへの呼び出しを呼び出し先コードへの直接ジャンプに焼き付けます-探しているもののような任意のタイプのオーバーロードを作成するためにフックするルックアップまたは決定プロセスはありません(Destroyより簡単なものを呼び出す以外に)嘲笑)。

于 2011-06-30T23:58:25.913 に答える
5

シンプルで、Typemock Isolator ++を使用します(免責事項-私はそこで働いています)

テストに1行追加して、偽造/モックしますDestroy

グローバルな使用:

FAKE_GLOBAL(Destroy);

公共の静力学の使用:

WHEN_CALLED(Third_Party::Destroy()).Ignore();

プライベート統計の使用:

PRIVATE_WHEN_CALLED(Third_Party::Destroy).Ignore();

追加のコードは必要ありません/条件付きコードはありません/別のリンクはありません。

于 2015-03-25T09:45:28.540 に答える
0

私はここで遊んでいますが、あなたのために働くかもしれない1つのアプローチは、独自のdestroy関数を提供し、Third_Party_Libポインターの周りにラッパークラスを追加することによってそれを支持して呼び出しを明確にすることです...

#include <iostream>

namespace Third_Party_Lib
{
    struct X { };
    void destroy(X*) { std::cout << "Third_Party_Lib::destroy()\n"; }
}

template <typename T>
struct Wrap
{
    Wrap(const T& t) : t_(t) { }
    operator T&() { return t_; }
    operator const T&() const { return t_; }
    T t_;
};

namespace Mine
{

#if TEST_MODE
    // this destroy will be called because it's a better match
    // not needing Wrap::operator T&...
    void destroy(Wrap<Third_Party_Lib::X*>) { std::cout << "Mine::destroy()\n"; }
#endif

    struct Q
    {
        Q(Third_Party_Lib::X* p) : p_(p) { }
        ~Q() { destroy(Wrap<Third_Party_Lib::X*>(p_)); }
        Third_Party_Lib::X* p_;
    };
}

#if TEST_MODE    
int main()
{
    Mine::Q q(new Third_Party_Lib::X);
}
#endif
于 2011-07-01T01:30:21.430 に答える
0

関数ポインタは、別の実装を置き換える方法を作成します。さらに、それは発信者には透過的です(彼らが電話のような本当に愚かなことをしているのでない限りsizeof(funcname))。

于 2011-07-01T01:33:51.823 に答える