18

同じ入力に対して同じ結果を返す検索アルゴリズムの2つの実装があるとしましょう。どちらも同じインターフェースを実装しています。

[TestClass]最終的に同じロジックで2つのテストファイルを作成するのではなく、単一を使用して両方の実装をテストするにはどうすればよいですか?

異なるコンストラクターパラメーターを使用してテストの1つを2回起動するようにMSUnitに指示できますか?
おそらく私はそれを何とかして(n)注入する必要がありますか?

4

6 に答える 6

16

抽象テスト クラスを使用します。

[TestClass]
public abstract class SearchTests
{
    private ISearcher _searcherUnderTest;

    [TestSetup]
    public void Setup()
    {
        _searcherUnderTest = CreateSearcher();
    }

    protected abstract ISearcher CreateSearcher();

    [TestMethod]
    public void Test1(){/*do stuff to _searcherUnderTest*/ }

    // more tests...

    [TestClass]
    public class CoolSearcherTests : SearcherTests
    {
         protected override ISearcher CreateSearcher()
         {
             return new CoolSearcher();
         }
    }

    [TestClass]
    public class LameSearcherTests : SearcherTests
    {
         protected override ISearcher CreateSearcher()
         {
             return new LameSearcher();
         }
    }
}
于 2013-03-24T21:18:33.873 に答える
2

質問に。のタグを付けましたが、について質問しNUnitますMSTest。あなたが求めていることは、NUnitのパラメータ化されたテストフィクスチャで達成できます。私はMSTestに精通していないため、同等のアプローチを提案できません。クイック検索を行うと、MSTestにこの機能がない可能性があります。

NUnitでは[TestFixture(...)]、さまざまなパラメータを使用してフィクスチャクラスに複数の属性を適用することにより、テストフィクスチャをパラメータ化します。これらのパラメーターは、フィクスチャーコンストラクターに渡されます。

渡すことができるパラメーターのタイプには制限があるため、アルゴリズムを指定する際に文字列を渡す必要があります。次に、コンストラクターで、検索アルゴリズムを提供するデリゲートまたはオブジェクトを、で使用されるメンバーフィールドに割り当てます。テスト。

例えば:

using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace MyTests
{
    public static class SearchAlgorithms
    {
        public static int DefaultSearch(int target, IList<int> data)
        {
            return data.IndexOf(target);
        }

        public static int BrokenSearch(int target, IList<int> data)
        {
            return 789;
        }
    }

    [TestFixture("forward")]
    [TestFixture("broken")]
    public class SearchTests
    {
        private Func<int, IList<int>, int> searchMethod;

        public SearchTests(string algorithmName)
        {
            if (algorithmName == "forward")
            {
                this.searchMethod = SearchAlgorithms.DefaultSearch;
                return;
            }

            if (algorithmName == "broken")
            {
                this.searchMethod = SearchAlgorithms.BrokenSearch;
            }
        }

        [Test]
        public void SearchFindsCorrectIndex()
        {
            Assert.AreEqual(
                1, this.searchMethod(2, new List<int> { 1, 2, 3 }));
        }

        [Test]
        public void SearchReturnsMinusOneWhenTargetNotPresent()
        {
            Assert.AreEqual(
                -1, this.searchMethod(4, new List<int> { 1, 2, 3 }));
        }
    }
}
于 2013-03-22T12:39:57.847 に答える
1

私はむしろ、それぞれ[TestMethod]が1[TestClass]つの実装のみをテストする2つの異なるものを使用したいと思います。このように、失敗したテストは、どの実装が間違っていたかを常に正しく示します。

于 2013-03-22T11:59:53.023 に答える
1

NUnit を使用している場合は、属性で宣言された変数を渡すことができます http://www.nunit.org/index.php?p=testCase&r=2.5.6

次のようなものを使用する場合:

[TestCase(1)]
[TestCase(2)]
public void Test(int algorithm)
{
//..dostuff
}

1 に対して 1 回、2 に対して 1 回実行される場合は、同じセットアップ/ティアダウンも使用します :)

MSTest には同等のものはありませんが、ここで説明されているように多少ごまかすことができます: MSTest には NUnit の TestCase と同等のものがありますか?

于 2013-03-22T12:02:10.413 に答える
0

このアプローチに非常に満足しているとは言えませんが、最終的には次のようになりました。次に、より良いアプローチを探しに行き、この質問を見つけました。このアプローチは、1) MS Test を使用している、2) テスト ロジックを 1 回しか記述していない、3) どの実装が失敗したかがわかります (テストをダブルクリックすると、適切なテスト クラスに移動します)、という基準を満たしています。 . このアプローチでは、基本クラスを使用してすべての実際のテスト ロジックを含め、次に各実装 (私は 3 つ) の派生クラスを使用して、基本インターフェイスに特定の実装を設定し、基本テスト メソッドをオーバーライドします。

[TestClass]
public abstract class SearchTestBase
{
    protected ISearcher Searcher { get; set; }

    [TestMethod]
    public virtual void Find_Results_Correct()
    {
        // Arrange (code here)
        // Act (single line here)
        var actual = Searcher.Results(input);
        // Assert
    }
}

(different file...)
[TestClass]
public class FastSearcherTest : SearcherTestBase
{
    [TestInitialize]
    public void TestInitialize()
    {
        Searcher = new FastSearcher();
    }

    [TestMethod]
    public override void Find_Results_Correct()
    {
        base.Find_Results_Correct();
    }
}

(different file...)
[TestClass]
public class ThoroughSearcherTest : SearcherTestBase
{
    [TestInitialize]
    public void TestInitialize()
    {
        Searcher = new ThoroughSearcher();
    }

    [TestMethod]
    public override void Find_Results_Correct()
    {
        base.Find_Results_Correct();
    }
}

このアプローチの気に入らない点は、テストを追加するたびに、各テスト ファイルに移動して、新しいテスト メソッドをオーバーライドする必要があることです。私が好きなのは、あなたが持っていた3つの要件です。テストを変更する必要がある場合は、ロジックを 1 か所だけ変更します。2 つのテストで 1 つのメソッドを呼び出すという同様のソリューションよりも、このソリューションの利点は、適切な実装をセットアップするためにコードを繰り返す必要がないことです。このソリューションでは、base.TestName() を呼び出す 1 行があり、2 行ではなく、1 行はサーチャーを設定し、もう 1 行はテストを呼び出します。また、Visual Studio を使用すると、これをはるかに高速に記述できます。「オーバーライド」と入力して、選択肢のリストを取得するだけです。オートコンプリートが残りを書いてくれます。

于 2017-01-08T18:01:07.047 に答える