Delen via


Ordereenheidtests

Soms wilt u misschien eenheidstests in een specifieke volgorde laten uitvoeren. In het ideale geval is de volgorde waarin eenheidstests worden uitgevoerd, niet van belang en is het raadzaam om het ordenen van eenheidstests te voorkomen. Het kan ook nodig zijn om dit te doen. In dat geval laat dit artikel zien hoe u testuitvoeringen kunt orden.

Notitie

Testvolgorde en testparallelisatie zijn afzonderlijke problemen. Het opgeven van een uitvoeringsvolgorde bepaalt de volgorde waarin tests worden gestart, maar als parallellisatie is ingeschakeld, kunnen meerdere tests nog steeds gelijktijdig worden uitgevoerd. Als u wilt garanderen dat tests één voor één worden uitgevoerd in de opgegeven volgorde, moet u ook parallelle uitvoering uitschakelen.

Als u liever door de broncode bladert, raadpleegt u de voorbeeldopslagplaats voor .NET Core unittests.

Aanbeveling

Naast de bestelmogelijkheden die in dit artikel worden beschreven, kunt u aangepaste afspeellijsten maken met Visual Studio als alternatief.

Alfabetisch orden

Notitie

MSTest voert tests sequentieel uit binnen een klasse. Als u parallellisme configureert met behulp van de <Parallelize> instelling in een .runsettings bestand, kunnen tests in verschillende klassen gelijktijdig worden uitgevoerd en heeft de volgorde alleen invloed op de volgorde binnen elke klasse.

MSTest detecteert tests in dezelfde volgorde waarin ze zijn gedefinieerd in de testklasse.

Wanneer u Test Explorer gebruikt (in Visual Studio of in Visual Studio Code), worden tests alfabetisch op testnaam gesorteerd.

Wanneer tests buiten Test Explorer worden uitgevoerd, worden tests uitgevoerd in de volgorde waarin ze zijn gedefinieerd in de testklasse.

Notitie

Een test met de naam Test14 zal worden uitgevoerd voordat Test2, ook al is het getal 2 minder dan 14. Dit komt doordat de volgorde van de testnaam gebruikmaakt van de tekstnaam van de test.

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);
    }
}

Vanaf MSTest 3.6 kunt u met een nieuwe optie runsettings tests uitvoeren op basis van testnamen, zowel in Testverkenners als op de opdrachtregel. Als u deze functie wilt inschakelen, voegt u de OrderTestsByNameInClass instelling toe aan het runettings-bestand:

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

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

</RunSettings>

Het xUnit-testframework biedt meer granulariteit en controle over testuitvoeringsvolgorde. U implementeert de ITestCaseOrderer en ITestCollectionOrderer interfaces om de volgorde van testcases voor een klasse of testverzamelingen te beheren.

Notitie

xUnit voert standaard testklassen parallel uit. Tests binnen één klasse worden altijd sequentieel uitgevoerd, dus ITestCaseOrderer bepaalt de volgorde binnen die klasse. Als u parallellisme wilt uitschakelen voor alle klassen, moet u dit toepassen [assembly: CollectionBehavior(DisableTestParallelization = true)] op assemblyniveau, bijvoorbeeld in AssemblyInfo.cs of in een bronbestand in uw testproject.

Alfabetisch sorteren op testcase

Als u testcases wilt bestellen op basis van hun methodenaam, implementeert u het ITestCaseOrderer en geeft u een bestelmechanisme op.

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);
}

Vervolgens stelt u in een testklasse de testcasevolgorde in met de TestCaseOrdererAttribute.

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);
    }
}

Alfabetisch sorteren op verzameling

Als u testverzamelingen wilt bestellen op basis van hun weergavenaam, implementeert u het ITestCollectionOrderer en geeft u een bestelmechanisme op.

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);
}

Aangezien testverzamelingen mogelijk parallel worden uitgevoerd, moet u testparallellisatie van de verzamelingen expliciet uitschakelen met de CollectionBehaviorAttribute. Geef vervolgens de implementatie op voor de TestCollectionOrdererAttribute.

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;
    }
}

Bestellen op aangepast kenmerk

Als u xUnit-tests met aangepaste kenmerken wilt bestellen, hebt u eerst een kenmerk nodig waarop u moet vertrouwen. Definieer een TestPriorityAttribute als volgt:

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;
}

Overweeg vervolgens de volgende PriorityOrderer implementatie van de ITestCaseOrderer interface.

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());
}

Vervolgens stelt u in een testklasse de testcasevolgorde in met de TestCaseOrdererAttribute naar de PriorityOrderer.

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);
    }
}

Volgorde op prioriteit

Notitie

NUnit voert standaard tests sequentieel uit binnen één thread. Tenzij u kenmerken hebt toegepast [Parallelizable] , is het [Order] kenmerk alleen voldoende om seriële uitvoering in de opgegeven volgorde te garanderen.

Als u tests expliciet wilt bestellen, biedt NUnit een OrderAttribute. Tests met dit kenmerk worden gestart vóór tests zonder. De orderwaarde wordt gebruikt om de volgorde te bepalen voor het uitvoeren van de eenheidstests.

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);
    }
}

Volgende stappen