19

デストラクタを単体テストする良い方法はありますか? たとえば、この(不自然な)例のようなクラスがあるとします:

class X
{
private:
    int *x;

public:
    X()
    {
         x = new int;
    }

    ~X()
    {
         delete x;
    }

    int *getX() {return x;}
    const int *getX() const {return x;}
};

#ifdef TEST で hpp ファイルを混乱させたり、カプセル化を壊したりせずに x が削除されることを確認するために、これを単体テストする良い方法はありますか? 私が見ている主な問題は、特にデストラクタが呼び出された時点でオブジェクトがスコープ外にあるため、 x が本当に削除されたかどうかを判断するのが難しいことです。

4

7 に答える 7

10

依存性注入については、何か言いたいことがあるかもしれません。コンストラクターでオブジェクト (この場合は int ですが、不自然な場合はユーザー定義型である可能性が高い) を作成する代わりに、オブジェクトがパラメーターとしてコンストラクターに渡されます。オブジェクトが後で作成される場合、ファクトリが X のコンストラクタに渡されます。

次に、単体テストを行うときに、モック オブジェクト (またはモック オブジェクトを作成するモック ファクトリ) を渡します。デストラクタは、それが呼び出されたという事実を記録します。そうでない場合、テストは失敗します。

もちろん、組み込み型をモック (または別の方法で置き換える) ことはできないため、この特定のケースでは良くありませんが、オブジェクト/ファクトリをインターフェイスで定義すれば可能です。

単体テストでのメモリリークのチェックは、他の人が言っているように、より高いレベルで行うことができます。しかし、それはデストラクタが呼び出されたことを確認するだけで、正しいデストラクタが呼び出されたことを証明するものではありません。したがって、たとえば、x メンバーの型のデストラクタで欠落している「仮想」宣言をキャッチすることはありません (繰り返しますが、単に int の場合は関係ありません)。

于 2008-11-23T02:40:05.473 に答える
10

あなたの問題は、現在の例がテストできないことだと思います。が削除されたかどうかを知りたいので、本当にモックxに置き換えることができる必要があります。xこれはおそらくintの少しOTTですが、実際の例では他のクラスがあると思います。テスト可能にするために、コンストラクターはインターフェイスXを実装するオブジェクトを要求する必要があります。int

template<class T>
class X
{
  T *x;
  public:
  X(T* inx)
    : x(inx)
  {
  }

  // etc
};

の値をモックするのが簡単にxなり、モックは正しい破棄のチェックを処理できます。

テスト可能なコードを作成するには、カプセル化を解除したり、恐ろしいハックに頼ったりする必要があると言う人には注意を払ってください。テストされたコードがテストされていないコードよりも優れていることは事実ですが、テスト可能なコードは何よりも優れており、ハックが少なく結合が少ない、より明確なコードが常に得られます。

于 2008-11-23T02:41:15.987 に答える
2

私は、テストに対して「どうしても必要な」アプローチをとる傾向があります。テストが必要な場合は、抽象化をリークしたり、カプセル化を破ったり、ハッキングしたりします... テストされたコードはきれいなコードよりも優れているためです。これを分割するメソッドを VaildateForTesting や OverrideForTesting のように名前を付けて、カプセル化の違反がテストのみを目的としていることを明確にすることがよくあります。

C++ でこれを行うには、デストラクタでシングルトンを呼び出して、それが破棄されたことを登録する以外の方法を知りません。弱参照を使用して C# でこれと同様のことを行う方法を思いつきました (このアプローチでは、カプセル化や抽象化に違反していません)。私は C++ との類似性を思いつくほど創造的ではありませんが、あなたはそうかもしれません。それが役に立ったら、素晴らしいです。そうでなければ、申し訳ありません。

http://houseofbilz.com/archive/2008/11/11/writing-tests-to-catch-memory-leaks-in-.net.aspx

于 2008-11-23T01:16:19.743 に答える
1

質問をした人には関係ありませんが、これを読んでいる他の人には役立つかもしれません。就職の面接でも同様の質問がありました。

メモリが限られていると仮定すると、次の方法を試すことができます。

  1. メモリ不足のメッセージで割り当てが失敗するまで(デストラクタに関連するテストを実行する前に)メモリを割り当て、テストを実行する前に使用可能なメモリのサイズを保存します。
  2. テストを実行します(コンストラクターを呼び出し、新しいインスタンスで必要に応じていくつかのアクションを実行します)。
  3. デストラクタを実行します。
  4. テストを実行する前に割り当てることができたのとまったく同じメモリを割り当てることができる場合は、割り当て部分を再度実行します(手順1と同様)。デストラクタは正常に機能します。

この方法は、メモリが限られている場合に(合理的に)機能します。そうでない場合は、少なくとも私の意見では不合理に思えます。

于 2012-08-27T12:12:41.003 に答える
1

この例では、独自のグローバルな new と delete を定義して計測します。

#ifdefs を避けるために、私はテスト クラスの友達を作りました。呼び出しの結果を確認するために、必要に応じて状態を設定/保存/取得できます。

于 2008-11-23T01:21:39.250 に答える
0

一部のコンパイラは、削除されたメモリをデバッグ モードで既知のパターンで上書きして、ダングリング ポインターへのアクセスを検出できるようにします。Visual C++ が 0xDD を使用していたことは知っていますが、しばらく使用していません。

あなたのテスト ケースでは、x のコピーを保存し、スコープ外に出して、*x == 0xDDDDDDDD であることを確認できます。

void testDestructor()
{
    int *my_x;
    {
        X object;
        my_x = object.getX();
    }
    CPPUNIT_ASSERT( *my_x == 0xDDDDDDDD );
}
于 2008-11-23T01:13:08.440 に答える
0

プラットフォームにとらわれない提案ではありませんが、過去に単体テスト中に CRT のヒープ チェック関数を呼び出して、テスト (またはおそらくテスト セット全体) の最後に、始める。ハンドル数などを確認するために、プラットフォームのインストルメンテーションで同様のことができる場合もあります。

于 2008-11-23T01:21:09.470 に答える