11

多数の配列やその他のクラス オブジェクト メンバーを含む複雑な .NET クラスがあるとします。このオブジェクトのディープ クローンを生成できるようにする必要があるため、Clone() メソッドを記述し、単純な BinaryFormatter シリアル化/デシリアライズを使用して実装します。または、エラーが発生しやすい他の手法を使用してディープ クローンを作成することもできます。そして、私はそれがテストされていることを確認したいと思います.

よし、じゃあ (よし、最初にやるべきだった) クローン作成をカバーするテストを書きたい。クラスのすべてのメンバーはプライベートであり、私のアーキテクチャは非常に優れている (!) ため、何百ものパブリック プロパティや他のアクセサーを記述する必要はありません。このクラスは、アプリケーションで必要ないため、IComparable または IEquatable ではありません。私の単体テストは、製品コードとは別のアセンブリにあります。

複製されたオブジェクトが適切なコピーであることをテストするために、人々はどのようなアプローチをとりますか? 「処女」オブジェクトまたはそのクローンのいずれかで呼び出せるように、クラスのすべての単体テストを作成しますか (またはクローンの必要性を発見したら書き直しますか)。クローニングの一部が十分に深くない場合、どのようにテストしますか? これは、後で見つけるのが困難なバグを引き起こす可能性のある種類の問題です。

4

6 に答える 6

2

テストの方法は、思いついたソリューションの種類によって異なります。カスタムクローンコードを作成し、それを各クローン可能タイプに手動で実装する必要がある場合は、それらのタイプのそれぞれのクローンを実際にテストする必要があります。あるいは、より一般的なルート(前述のリフレクションが適合する可能性が高い)に進むことにした場合、テストは、クローンシステムが処理する必要のある特定のシナリオのみをテストする必要があります。

特定の質問に答えるには:

'virgin'オブジェクトまたはそのクローンのいずれかで呼び出すことができるように、クラスのすべての単体テストを記述しますか(または、クローンの必要性を発見したら書き直しますか)?

元のオブジェクトと複製されたオブジェクトの両方で実行できるすべてのメソッドのテストが必要です。各テストのロジックを手動で更新しなくても、これをサポートする簡単なテスト設計を設定するのは非常に簡単であることに注意してください。

クローンの一部が十分に深くないかどうかをどのようにテストしますか?これは、後で見つけるのが恐ろしいバグを引き起こす可能性がある一種の問題です。

選択したクローン作成方法によって異なります。複製可能なタイプを手動で更新する必要がある場合は、各タイプが期待するすべての(そして唯一の)メンバーを複製していることをテストする必要があります。一方、クローンフレームワークをテストしている場合は、サポートする必要のある各シナリオをテストするために、いくつかのテストクローン可能タイプを作成します。

于 2008-08-14T14:01:59.097 に答える
2

それほど多くの作業を必要としない、非常に明白な解決策があります。

  1. オブジェクトをバイナリ形式にシリアル化します。
  2. オブジェクトを複製します。
  3. クローンをバイナリ形式にシリアル化します。
  4. バイトを比較します。

シリアライゼーションが機能すると仮定すると、それを使用してクローンを作成しているため、より適切に維持できます。実際、クラスの構造への変更から完全にカプセル化されます。

于 2010-03-23T04:22:25.513 に答える
1

私は、元のオブジェクトと複製されたオブジェクトで組み込みのシリアライザーの 1 つを使用する単体テストを作成し、シリアル化された表現が等しいかどうかをチェックするのが好きです (バイナリ フォーマッタの場合は、バイト配列を比較するだけです)。これは、オブジェクトがまだシリアライズ可能な場合にうまく機能し、パフォーマンス上の理由からカスタムのディープ クローンに変更するだけです。

さらに、このようなものを使用して、すべての Clone 実装にデバッグ モード チェックを追加するのが好きです。

[Conditional("DEBUG")]
public static void DebugAssertValueEquality<T>(T current, T other, bool expected, 
                                               params string[] ignoredFields) {
    if (null == current) 
    { throw new ArgumentNullException("current"); }
    if (null == ignoredFields)
    { ignoredFields = new string[] { }; }

    FieldInfo lastField = null;
    bool test;
    if (object.ReferenceEquals(other, null))
    { Debug.Assert(false == expected, "The other object was null"); return; }
    test = true;
    foreach (FieldInfo fi in current.GetType().GetFields(BindingFlags.Instance)) {
        if (test = false) { break; }
        if (0 <= Array.IndexOf<string>(ignoredFields, fi.Name))
        { continue; }
        lastField = fi;
        object leftValue = fi.GetValue(current);
        object rightValue = fi.GetValue(other);
        if (object.ReferenceEquals(null, leftValue)) {
            if (!object.ReferenceEquals(null, rightValue))
            { test = false; }
        }
        else if (object.ReferenceEquals(null, rightValue))
        { test = false; }
        else {
            if (!leftValue.Equals(rightValue))
            { test = false; }
        }
    }
    Debug.Assert(test == expected, string.Format("field: {0}", lastField));
}

このメソッドは、ネストされたメンバーでの Equals の正確な実装に依存していますが、私の場合、クローン可能なものはすべて同等です

于 2008-08-23T12:03:01.513 に答える
1

私は通常Equals()、2 つのオブジェクトを詳細に比較するために実装します。実稼働コードでは必要ないかもしれませんが、後で役立つ可能性があり、テスト コードはよりクリーンになります。

于 2012-09-18T13:04:32.123 に答える
1

クローンが正しいかどうかを判断するテストを 1 つ作成するだけです。クラスがシールされていない場合は、クラスを拡張してハーネスを作成し、子クラス内のすべての内部を公開できます。または、リフレクション (yech) を使用するか、MSTest のアクセサー ジェネレーターを使用することもできます。

オブジェクトを複製してから、オブジェクトが持つすべてのプロパティと変数を調べて、正しくコピーまたは複製されたかどうかを判断する必要があります。

于 2008-08-14T12:56:06.640 に答える