Compartir a través de


Ordenar ejecución de pruebas unitarias

En ocasiones, puede que desee que las pruebas unitarias se ejecuten en un orden específico. Idealmente, el orden en que se ejecutan las pruebas unitarias no importa, y el procedimiento recomendado es evitar la ordenación de las pruebas unitarias. En cualquier caso, puede que sea necesario hacerlo. En ese caso, en este artículo se muestra cómo ordenar las series de pruebas.

Nota:

La ordenación de pruebas y la paralelización de pruebas son problemas independientes. Si se especifica un orden de ejecución, se determina la secuencia en la que se inician las pruebas, pero si está habilitada la paralelización, se pueden seguir ejecutando varias pruebas simultáneamente. Para garantizar que las pruebas se ejecuten una a la vez en el orden especificado, también debe deshabilitar la paralelización.

Si prefiere examinar el código fuente, consulte el repositorio de ejemplo order .NET Pruebas unitarias principales.

Sugerencia

Además de las funcionalidades de ordenación que se describen en este artículo, considere crear listas de reproducción personalizadas con Visual Studio como alternativa.

Orden alfabético

Nota:

MSTest ejecuta pruebas secuencialmente dentro de una clase de forma predeterminada. Si configura paralelismo mediante la configuración de un archivo, las pruebas entre clases se pueden ejecutar simultáneamente y el orden afecta solo a la secuencia dentro de cada clase.

MSTest detecta pruebas en el mismo orden en el que se están definidas en la clase de prueba.

Cuando se ejecuta a través del Explorador de pruebas (en Visual Studio o en Visual Studio Code), las pruebas se ordenan en orden alfabético en función de su nombre de prueba.

Cuando se ejecutan fuera del Explorador de pruebas, las pruebas se ejecutan en el orden en que están definidas en la clase de pruebas.

Nota:

Una prueba denominada se ejecutará antes de aunque el número sea inferior a . Esto se debe a que la ordenación de los nombres de las pruebas utiliza el nombre de texto de la prueba.

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

A partir de MSTest 3.6, una nueva opción runsettings permite ejecutar pruebas por nombres de prueba tanto en exploradores de pruebas como en la línea de comandos. Para habilitar esta característica, agregue la configuración al archivo runsettings:

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

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

</RunSettings>

El marco de pruebas de xUnit permite más nivel de detalle y control del orden de la serie de pruebas. Implemente las interfaces y para controlar el orden de los casos de prueba de una clase o colecciones de prueba.

Nota:

xUnit ejecuta clases de prueba en paralelo de forma predeterminada. Las pruebas dentro de una sola clase siempre se ejecutan secuencialmente, por lo que controla la secuencia dentro de esa clase. Para deshabilitar el paralelismo en todas las clases, aplique en el nivel de ensamblado, por ejemplo, en o en cualquier archivo de origen del proyecto de prueba.

Orden alfabético por caso de prueba

Para ordenar los casos de prueba por su nombre de método, implemente y proporcione un mecanismo de ordenación.

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

Después, en una clase de prueba, establezca el orden de los casos de prueba con .

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

Orden alfabético de la colección

Para ordenar las colecciones de pruebas por su nombre para mostrar, implemente y proporcione un mecanismo de ordenación.

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

Como las colecciones de pruebas se ejecutan potencialmente en paralelo, debe deshabilitar explícitamente la paralelización de las colecciones con . A continuación, especifique la implementación en .

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

Orden por atributo personalizado

Para ordenar las pruebas de xUnit con atributos personalizados, primero necesita un atributo en el que confiar. Defina como se indica a continuación:

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

A continuación, tenga en cuenta la siguiente implementación de la interfaz .

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

Después, en una clase de prueba, establezca el orden de los casos de prueba con en .

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

Orden por prioridad

Nota:

NUnit ejecuta pruebas secuencialmente dentro de un único subproceso de forma predeterminada. A menos que haya aplicado atributos, el atributo solo es suficiente para garantizar la ejecución en serie en la secuencia especificada.

Para ordenar las pruebas de forma explícita, NUnit proporciona . Las pruebas con este atributo se inician antes que las pruebas sin él. El valor de orden se usa para determinar el orden de ejecución de las pruebas unitarias.

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

Pasos siguientes

Cobertura de pruebas unitarias del código