7

私はC#で一連のコレクションクラスを作成しています。各クラスは同様のカスタムインターフェイスを実装しています。インターフェイスの単体テストの単一のコレクションを作成し、それらすべてをいくつかの異なる実装で自動的に実行することは可能ですか?実装ごとに重複するテストコードを避けたいと思います。

これを実現するために、フレームワーク(NUnitなど)またはVisualStudio拡張機能を調べたいと思います。


同じことをしたいと思っている人のために、私はavandeursenの受け入れられた解決策に基づいた私の具体的な解決策を答えとして投稿しました。

4

4 に答える 4

6

はい、それは可能です。秘訣は、ユニットクラスのテスト階層をコードのクラス階層に従わせることです。

ItfクラスC1とを実装するインターフェースがあると仮定しましょうC2

Itfまず、 ( )のテストクラスを作成しますItfTest。実際にテストを実行するには、Itfインターフェイスのモック実装を作成する必要があります。

この中のすべてのテストは、 (!)ItfTestの実装に合格する必要があります。Itfそうでない場合、実装はリスコフの置換原則(Martinのオブジェクト指向設計のSOLID原則 の「L」)に準拠していません。

したがって、のテストケースを作成するためC1に、C1Testクラスはを拡張できますItfTest。拡張機能は、モックオブジェクトの作成をオブジェクトの作成に置き換える必要がありC1ます(オブジェクトを挿入するか、GoFファクトリメソッドを使用します)。このようにして、すべてのItfTestケースがタイプのインスタンスに適用されますC1。さらに、C1Testクラスには、に固有の追加のテストケースを含めることができますC1

同様にC2。そして、より深いネストされたクラスとインターフェースに対してこのトリックを繰り返すことができます。

参照:BinderのPolymorphic Server Testパターン、およびMcGregorのPACT-コンポーネントテスト用の並列アーキテクチャ。

于 2012-02-20T20:18:46.033 に答える
2

Joeの回答を拡張すると、MBUnitのRowTestと同様の方法で、NUnitの[TestCaseSource]属性を使用できます。そこにクラス名を含むテストケースソースを作成できます。次に、TestCaseSource属性を持つすべてのテストを装飾できます。次に、Activator.CreateInstanceを使用して、インターフェイスにキャストすることができ、設定されます。

このようなもの(注-頭をコンパイル)

string[] MyClassNameList = { "Class1", "Class2" };

[TestCaseSource(MyClassNameList)]
public void Test1(string className)
{
    var instance = Activator.CreateInstance(Type.FromName(className)) as IMyInterface;

    ...
}
于 2012-02-20T20:29:37.290 に答える
2

これを行うには、 MBUnitの[RowTest]属性を使用できます。次の例は、インスタンス化するインターフェイス実装クラスを示す文字列をメソッドに渡してから、リフレクションを介してこのクラスを作成する場所を示しています。

[RowTest]
[Row("Class1")]
[Row("Class2")]
[Row("Class3")]
public void TestMethod(string type)
{
   IMyInterface foo = Activator.CreateInstance(Type.GetType(type)) as IMyInterface;

   //Do tests on foo:
}

[Row]属性では、テスト用の入力値やメソッド呼び出しによって返される期待値など、任意の数の入力パラメーターを渡すことができます。対応するタイプの引数をテストメソッドの入力引数として追加する必要があります。

于 2012-02-20T20:14:27.157 に答える
2

これは、avandeursenの回答に基づいた私の具体的な実装です。

[TestClass]
public abstract class IMyInterfaceTests
{
    protected abstract IMyInterface CreateInstance();

    [TestMethod]
    public void SomeTest()
    {
        IMyInterface instance = CreateInstance();
        // Run the test
    }
}

次に、各インターフェイス実装は次のテストクラスを定義します。

[TestClass]
public class MyImplementationTests : IMyInterfaceTests
{
    protected override IMyInterface CreateInstance()
    {
        return new MyImplementation();
    }
}

SomeTestTestClassから派生したコンクリートごとに1回実行されIMyInterfaceTestsます。抽象基本クラスを使用することで、モック実装の必要性を回避します。TestClassAttribute必ず両方のクラスに追加してください。そうしないと、機能しません。最後に、必要に応じて、実装固有のテスト(コンストラクターなど)を子クラスに追加できます。

于 2012-02-21T18:20:25.400 に答える