次の方法で共有


単体テストの順序を設定する

場合によっては、単体テストを特定の順序で実行する必要があります。 理想的には、単体テストの実行順序が問題にならないことが望ましく、単体テストを順序付けないのがベスト プラクティスです。 そうはいっても、それを行うことが必要になる場合があります。 この記事では、そのような場合にテストの実行順序を設定する方法について説明します。

テストの順序付けとテストの並列化は、個別の懸念事項です。 実行順序を指定すると、テストを開始するシーケンスが決まりますが、並列化が有効になっている場合でも、複数のテストを同時に実行できます。 指定した順序でテストが一度に 1 つずつ実行されるようにするには、並列化も無効にする必要があります。

ソース コードを参照する場合は、order .NET Core 単体テストサンプル リポジトリを参照してください。

ヒント

この記事で説明する順序付け機能に加えて、代わりに Visual Studio を使用してカスタム プレイリストを作成することを検討してください。

アルファベット順に設定する

MSTest は、既定でクラス内でテストを順番に実行します。 ファイルの設定を使用して並列処理を構成すると、クラス間のテストを同時に実行でき、順序付けは各クラス内のシーケンスにのみ影響します。

MSTest は、テスト クラスでの定義と同じ順序でテストを検出します。

テスト エクスプローラー (Visual Studio または Visual Studio Code) を使用して実行する場合、テストはテスト名に基づいてアルファベット順に並べ替えられます。

テスト エクスプローラー以外で実行する場合、テストの実行はテスト クラスで定義されている順序になります。

番号 の方が より小さくても、 という名前のテストが より前に実行されます。 これは、テスト名の順序の設定ではテストのテキスト名が使用されるためです。

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MSTest.Project;

[TestClass]
public class ByAlphabeticalOrder
{
    public static bool Test1Called;
    public static bool Test2Called;
    public static bool Test3Called;

    [TestMethod]
    public void Test2()
    {
        Test2Called = true;

        Assert.IsTrue(Test1Called);
        Assert.IsFalse(Test3Called);
    }

    [TestMethod]
    public void Test1()
    {
        Test1Called = true;

        Assert.IsFalse(Test2Called);
        Assert.IsFalse(Test3Called);
    }

    [TestMethod]
    public void Test3()
    {
        Test3Called = true;

        Assert.IsTrue(Test1Called);
        Assert.IsTrue(Test2Called);
    }
}

MSTest 3.6 以降では、新しい runsettings オプションを使用すると、テスト エクスプローラーとコマンド ラインの両方でテスト名でテストを実行できます。 この機能を有効にするには、runsettings ファイルに 設定を追加します。

<?xml version="1.0" encoding="utf-8"?>
<RunSettings>

  <MSTest>
    <OrderTestsByNameInClass>true</OrderTestsByNameInClass>
  </MSTest>

</RunSettings>

xUnit テスト フレームワークでは、テストの実行順序をさらに細かく制御できます。 クラスのテスト ケースまたはテスト コレクションの順序を制御するには、 インターフェイスと インターフェイスを実装します。

xUnit では、既定でテスト クラスが並列で実行されます。 1 つのクラス内のテストは常に順番に実行されるため、 はそのクラス内のシーケンスを制御します。 すべてのクラスで並列処理を無効にするには、アセンブリ レベルで例えば "AssemblyInfo.cs" またはテスト プロジェクト内の任意のソースファイルに適用します。

テスト ケースのアルファベット順に順序を設定する

メソッド名でテスト ケースの順序を設定するには、 を実装し、順序設定のメカニズムを提供します。

using Xunit.Abstractions;
using Xunit.Sdk;

namespace XUnit.Project.Orderers;

public class AlphabeticalOrderer : ITestCaseOrderer
{
    public IEnumerable<TTestCase> OrderTestCases<TTestCase>(
        IEnumerable<TTestCase> testCases) where TTestCase : ITestCase =>
        testCases.OrderBy(testCase => testCase.TestMethod.Method.Name);
}

その後、テスト クラスで、 を使用してテスト ケースの順序を設定します。

using Xunit;

namespace XUnit.Project;

[TestCaseOrderer(
    ordererTypeName: "XUnit.Project.Orderers.AlphabeticalOrderer",
    ordererAssemblyName: "XUnit.Project")]
public class ByAlphabeticalOrder
{
    public static bool Test1Called;
    public static bool Test2Called;
    public static bool Test3Called;

    [Fact]
    public void Test1()
    {
        Test1Called = true;

        Assert.False(Test2Called);
        Assert.False(Test3Called);
    }

    [Fact]
    public void Test2()
    {
        Test2Called = true;

        Assert.True(Test1Called);
        Assert.False(Test3Called);
    }

    [Fact]
    public void Test3()
    {
        Test3Called = true;

        Assert.True(Test1Called);
        Assert.True(Test2Called);
    }
}

コレクションのアルファベット順に順序を設定する

表示名でテスト コレクションの順序を設定するには、 を実装し、順序設定のメカニズムを提供します。

using Xunit;
using Xunit.Abstractions;

namespace XUnit.Project.Orderers;

public class DisplayNameOrderer : ITestCollectionOrderer
{
    public IEnumerable<ITestCollection> OrderTestCollections(
        IEnumerable<ITestCollection> testCollections) =>
        testCollections.OrderBy(collection => collection.DisplayName);
}

テスト コレクションは並列に実行される可能性があるため、 を使用して、コレクションのテスト並列化を明示的に無効にする必要があります。 その後、 の実装を指定します。

using Xunit;

// Need to turn off test parallelization so we can validate the run order
[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: TestCollectionOrderer(
    ordererTypeName: "XUnit.Project.Orderers.DisplayNameOrderer",
    ordererAssemblyName: "XUnit.Project")]

namespace XUnit.Project;

[Collection("Xzy Test Collection")]
public class TestsInCollection1
{
    public static bool Collection1Run;

    [Fact]
    public static void Test()
    {
        Assert.True(TestsInCollection2.Collection2Run);     // Abc
        Assert.True(TestsInCollection3.Collection3Run);     // Mno
        Assert.False(TestsInCollection1.Collection1Run);    // Xyz

        Collection1Run = true;
    }
}

[Collection("Abc Test Collection")]
public class TestsInCollection2
{
    public static bool Collection2Run;

    [Fact]
    public static void Test()
    {
        Assert.False(TestsInCollection2.Collection2Run);    // Abc
        Assert.False(TestsInCollection3.Collection3Run);    // Mno
        Assert.False(TestsInCollection1.Collection1Run);    // Xyz

        Collection2Run = true;
    }
}

[Collection("Mno Test Collection")]
public class TestsInCollection3
{
    public static bool Collection3Run;

    [Fact]
    public static void Test()
    {
        Assert.True(TestsInCollection2.Collection2Run);     // Abc
        Assert.False(TestsInCollection3.Collection3Run);    // Mno
        Assert.False(TestsInCollection1.Collection1Run);    // Xyz

        Collection3Run = true;
    }
}

カスタム属性で順序を設定する

カスタム属性で xUnit テストの順序を設定するには、その前に、利用する属性が存在している必要があります。 次のように を定義します。

namespace XUnit.Project.Attributes;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TestPriorityAttribute : Attribute
{
    public int Priority { get; private set; }

    public TestPriorityAttribute(int priority) => Priority = priority;
}

次に、 インターフェイスの の実装を次のようにします。

using Xunit.Abstractions;
using Xunit.Sdk;
using XUnit.Project.Attributes;

namespace XUnit.Project.Orderers;

public class PriorityOrderer : ITestCaseOrderer
{
    public IEnumerable<TTestCase> OrderTestCases<TTestCase>(
        IEnumerable<TTestCase> testCases) where TTestCase : ITestCase
    {
        string assemblyName = typeof(TestPriorityAttribute).AssemblyQualifiedName!;
        var sortedMethods = new SortedDictionary<int, List<TTestCase>>();
        foreach (TTestCase testCase in testCases)
        {
            int priority = testCase.TestMethod.Method
                .GetCustomAttributes(assemblyName)
                .FirstOrDefault()
                ?.GetNamedArgument<int>(nameof(TestPriorityAttribute.Priority)) ?? 0;

            GetOrCreate(sortedMethods, priority).Add(testCase);
        }

        foreach (TTestCase testCase in
            sortedMethods.Keys.SelectMany(
                priority => sortedMethods[priority].OrderBy(
                    testCase => testCase.TestMethod.Method.Name)))
        {
            yield return testCase;
        }
    }

    private static TValue GetOrCreate<TKey, TValue>(
        IDictionary<TKey, TValue> dictionary, TKey key)
        where TKey : struct
        where TValue : new() =>
        dictionary.TryGetValue(key, out TValue? result)
            ? result
            : (dictionary[key] = new TValue());
}

その後、テスト クラスで、 を にしてテスト ケースの順序を設定します。

using Xunit;
using XUnit.Project.Attributes;

namespace XUnit.Project;

[TestCaseOrderer(
    ordererTypeName: "XUnit.Project.Orderers.PriorityOrderer",
    ordererAssemblyName: "XUnit.Project")]
public class ByPriorityOrder
{
    public static bool Test1Called;
    public static bool Test2ACalled;
    public static bool Test2BCalled;
    public static bool Test3Called;

    [Fact, TestPriority(5)]
    public void Test3()
    {
        Test3Called = true;

        Assert.True(Test1Called);
        Assert.True(Test2ACalled);
        Assert.True(Test2BCalled);
    }

    [Fact, TestPriority(0)]
    public void Test2B()
    {
        Test2BCalled = true;

        Assert.True(Test1Called);
        Assert.True(Test2ACalled);
        Assert.False(Test3Called);
    }

    [Fact]
    public void Test2A()
    {
        Test2ACalled = true;

        Assert.True(Test1Called);
        Assert.False(Test2BCalled);
        Assert.False(Test3Called);
    }

    [Fact, TestPriority(-5)]
    public void Test1()
    {
        Test1Called = true;

        Assert.False(Test2ACalled);
        Assert.False(Test2BCalled);
        Assert.False(Test3Called);
    }
}

優先順位で順序を設定する

NUnit は、既定で 1 つのスレッド内でテストを順番に実行します。 属性を適用していない限り、指定したシーケンスでのシリアル実行を保証するには、属性だけで十分です。

テストの順序を明示的に設定できるように、NUnit には が用意されています。 この属性が指定されているテストは、指定されていないテストより前に開始されます。 順序の値を使用して、単体テストを実行する順序が決定されます。

using NUnit.Framework;

namespace NUnit.Project;

public class ByOrder
{
    public static bool Test1Called;
    public static bool Test2ACalled;
    public static bool Test2BCalled;
    public static bool Test3Called;

    [Test, Order(5)]
    public void Test1()
    {
        Test1Called = true;

        Assert.That(Test2ACalled, Is.False);
        Assert.That(Test2BCalled, Is.True);
        Assert.That(Test3Called, Is.True);
    }

    [Test, Order(0)]
    public void Test2B()
    {
        Test2BCalled = true;

        Assert.That(Test1Called, Is.False);
        Assert.That(Test2ACalled, Is.False);
        Assert.That(Test3Called, Is.True);
    }

    [Test]
    public void Test2A()
    {
        Test2ACalled = true;

        Assert.That(Test1Called, Is.True);
        Assert.That(Test2BCalled, Is.True);
        Assert.That(Test3Called, Is.True);
    }

    [Test, Order(-5)]
    public void Test3()
    {
        Test3Called = true;

        Assert.That(Test1Called, Is.False);
        Assert.That(Test2ACalled, Is.False);
        Assert.That(Test2BCalled, Is.False);
    }
}

次のステップ

単体テストのコード カバレッジ