Compartir a través de


Pruebas controladas por datos en MSTest

Las pruebas controladas por datos permiten ejecutar el mismo método de prueba con varios conjuntos de datos de entrada. En lugar de escribir métodos de prueba independientes para cada caso de prueba, defina la lógica de prueba una vez y proporcione entradas diferentes a través de atributos o orígenes de datos externos.

Información general

MSTest proporciona varios atributos para las pruebas controladas por datos:

Atributo Caso de uso Más adecuado para
DataRow Datos de prueba en línea Casos de prueba simples y estáticos
DynamicData Datos de métodos, propiedades o campos Datos de prueba complejos o calculados
TestDataRow<T> Datos mejorados con metadatos Casos de prueba que necesitan nombres para mostrar o categorías
DataSource Archivos o bases de datos de datos externos Escenarios obsoletos con fuentes de datos externas
ITestDataSource Atributos de origen de datos personalizados Escenarios totalmente basados en datos personalizados

Sugerencia

Para las pruebas combinatorias (probar todas las combinaciones de varios conjuntos de parámetros), use el paquete NuGet Combinatorial.MSTest de código abierto. Este paquete mantenido por la comunidad está disponible en GitHub , pero Microsoft no lo mantiene.

DataRowAttribute

DataRowAttribute permite ejecutar el mismo método de prueba con varias entradas diferentes. Aplique uno o varios DataRow atributos a un método de prueba y combínelo con .TestMethodAttribute

El número y los tipos de argumentos deben coincidir exactamente con la signatura del método de prueba.

Sugerencia

Analizadores relacionados:

  • MSTEST0014 valida que DataRow los argumentos coinciden con la firma del método de prueba.
  • MSTEST0042 detecta entradas duplicadas DataRow que ejecutarían el mismo caso de prueba varias veces.

Uso básico

[TestClass]
public class CalculatorTests
{
    [TestMethod]
    [DataRow(1, 2, 3)]
    [DataRow(0, 0, 0)]
    [DataRow(-1, 1, 0)]
    [DataRow(100, 200, 300)]
    public void Add_ReturnsCorrectSum(int a, int b, int expected)
    {
        var calculator = new Calculator();
        Assert.AreEqual(expected, calculator.Add(a, b));
    }
}

Tipos de argumento admitidos

DataRow admite varios tipos de argumentos, incluidos primitivos, cadenas, matrices y valores NULL:

[TestClass]
public class DataRowExamples
{
    [TestMethod]
    [DataRow(1, "message", true, 2.0)]
    public void TestWithMixedTypes(int i, string s, bool b, float f)
    {
        // Test with different primitive types
    }

    [TestMethod]
    [DataRow(new string[] { "line1", "line2" })]
    public void TestWithArray(string[] lines)
    {
        Assert.AreEqual(2, lines.Length);
    }

    [TestMethod]
    [DataRow(null)]
    public void TestWithNull(object o)
    {
        Assert.IsNull(o);
    }

    [TestMethod]
    [DataRow(new string[] { "a", "b" }, new string[] { "c", "d" })]
    public void TestWithMultipleArrays(string[] input, string[] expected)
    {
        // Starting with MSTest v3, two arrays don't need wrapping
    }
}

Uso de parámetros para argumentos variables

Use la params palabra clave para aceptar un número variable de argumentos:

[TestClass]
public class ParamsExample
{
    [TestMethod]
    [DataRow(1, 2, 3, 4)]
    [DataRow(10, 20)]
    [DataRow(5)]
    public void TestWithParams(params int[] values)
    {
        Assert.IsTrue(values.Length > 0);
    }
}

Nombres de pantalla personalizados

Establezca la DisplayName propiedad para personalizar cómo aparecen los casos de prueba en el Explorador de pruebas:

[TestClass]
public class DisplayNameExample
{
    [TestMethod]
    [DataRow(1, 2, DisplayName = "Functional Case FC100.1")]
    [DataRow(3, 4, DisplayName = "Edge case: small numbers")]
    public void TestMethod(int i, int j)
    {
        Assert.IsTrue(i < j);
    }
}

Sugerencia

Para obtener más control sobre los metadatos de prueba, considere la posibilidad de usar TestDataRow<T> con DynamicData. TestDataRow<T> admite nombres para mostrar junto con categorías de prueba y omitir mensajes para casos de prueba individuales.

Omitir casos de prueba específicos

A partir de MSTest v3.8, use la IgnoreMessage propiedad para omitir filas de datos específicas:

[TestClass]
public class IgnoreDataRowExample
{
    [TestMethod]
    [DataRow(1, 2)]
    [DataRow(3, 4, IgnoreMessage = "Temporarily disabled - bug #123")]
    [DataRow(5, 6)]
    public void TestMethod(int i, int j)
    {
        // Only the first and third data rows run
        // The second is skipped with the provided message
    }
}

DynamicDataAttribute

DynamicDataAttribute permite proporcionar datos de prueba de métodos, propiedades o campos. Utilice este atributo cuando los datos de prueba sean complejos, calculados dinámicamente o demasiado detallados para atributos en línea DataRow.

Tipos de origen de datos admitidos

El origen de datos puede devolver cualquier IEnumerable<T> donde T sea uno de los tipos enumerados en la siguiente tabla. Cualquier colección que implemente IEnumerable<T> funciona, incluidas List<T>, matrices como T[], o tipos de colección personalizados. Elija según sus necesidades:

Tipo de retorno Seguridad de tipos Compatibilidad con metadatos Más adecuado para
ValueTuple (por ejemplo, (int, string)) Tiempo de compilación No Mayoría de los escenarios: sintaxis simple con comprobación de tipos completa
Tuple<...> Tiempo de compilación No Cuando no se puede usar ValueTuple
TestDataRow<T> Tiempo de compilación Casos de prueba que necesitan nombres para mostrar, categorías o omitir mensajes
object[] Solo tiempo de ejecución No Código heredado: evite las nuevas pruebas.

Sugerencia

Para los nuevos métodos de datos de prueba, use ValueTuple para casos simples o TestDataRow<T> cuando necesite metadatos. Evite object[] porque carece de comprobación de tipos en tiempo de compilación y puede provocar errores en tiempo de ejecución a partir de errores de coincidencia de tipos.

Orígenes de datos

El origen de datos puede ser un método, una propiedad o un campo. Los tres son intercambiables: elija en función de su preferencia:

[TestClass]
public class DynamicDataExample
{
    // Method - best for computed or yielded data
    public static IEnumerable<(int Value, string Name)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }

    // Property - concise for static data
    public static IEnumerable<(int Value, string Name)> TestDataProperty =>
    [
        (1, "first"),
        (2, "second")
    ];

    // Field - simplest for static data
    public static IEnumerable<(int Value, string Name)> TestDataField =
    [
        (1, "first"),
        (2, "second")
    ];

    [TestMethod]
    [DynamicData(nameof(GetTestData))]
    public void TestWithMethod(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }

    [TestMethod]
    [DynamicData(nameof(TestDataProperty))]
    public void TestWithProperty(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }

    [TestMethod]
    [DynamicData(nameof(TestDataField))]
    public void TestWithField(int value, string name)
    {
        Assert.IsTrue(value > 0);
    }
}

Nota:

Los métodos, propiedades y campos del origen de datos deben ser public static y devolver un IEnumerable<T> de un tipo admitido.

Sugerencia

Analizador relacionado: MSTEST0018 valida que el origen de datos existe, es accesible y tiene la firma correcta.

Origen de datos de una clase diferente

Especifique una clase diferente mediante el parámetro type:

public class TestDataProvider
{
    public static IEnumerable<object[]> GetTestData()
    {
        yield return new object[] { 1, "first" };
        yield return new object[] { 2, "second" };
    }
}

[TestClass]
public class DynamicDataExternalExample
{
    [TestMethod]
    [DynamicData(nameof(TestDataProvider.GetTestData), typeof(TestDataProvider))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }
}

Nombres para mostrar personalizados

Personalice los nombres para mostrar del caso de prueba mediante la DynamicDataDisplayName propiedad :

using System.Reflection;

[TestClass]
public class DynamicDataDisplayNameExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestData), DynamicDataDisplayName = nameof(GetDisplayName))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }

    public static IEnumerable<object[]> GetTestData()
    {
        yield return new object[] { 1, "first" };
        yield return new object[] { 2, "second" };
    }

    public static string GetDisplayName(MethodInfo methodInfo, object[] data)
    {
        return $"{methodInfo.Name} with value {data[0]} and '{data[1]}'";
    }
}

Nota:

El método de nombre para mostrar debe ser public static, devolver un stringy aceptar dos parámetros: MethodInfo y object[].

Sugerencia

Para obtener un enfoque más sencillo para los nombres para mostrar personalizados, considere la posibilidad de usar TestDataRow<T> con su DisplayName propiedad en lugar de un método independiente.

Omitir todos los casos de prueba de un origen de datos

A partir de MSTest v3.8, use IgnoreMessage para omitir todos los casos de prueba:

[TestClass]
public class IgnoreDynamicDataExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestData), IgnoreMessage = "Feature not ready")]
    public void TestMethod(int value1, string value2)
    {
        // All test cases from GetTestData are skipped
    }

    public static IEnumerable<object[]> GetTestData()
    {
        yield return new object[] { 1, "first" };
        yield return new object[] { 2, "second" };
    }
}

Sugerencia

Para omitir casos de prueba individuales, use TestDataRow<T> con la propiedad IgnoreMessage. Consulte la sección TestDataRow<T> .

TestDataRow

La TestDataRow<T> clase proporciona un control mejorado sobre los datos de prueba en pruebas controladas por datos. Use IEnumerable<TestDataRow<T>> como tipo de valor devuelto del origen de datos para especificar:

  • Nombres para mostrar personalizados: establecer un nombre para mostrar único por caso de prueba
  • Categorías de prueba: Adjuntar metadatos a casos de prueba individuales
  • Omitir mensajes: omitir casos de prueba específicos por motivos
  • Datos seguros para tipos: usar genéricos para datos de prueba fuertemente tipados

Uso básico

[TestClass]
public class TestDataRowExample
{
    [TestMethod]
    [DynamicData(nameof(GetTestDataRows))]
    public void TestMethod(int value1, string value2)
    {
        Assert.IsTrue(value1 > 0);
    }

    public static IEnumerable<TestDataRow> GetTestDataRows()
    {
        yield return new TestDataRow((1, "first"))
        {
            DisplayName = "Test Case 1: Basic scenario",
        };

        yield return new TestDataRow((2, "second"))
        {
            DisplayName = "Test Case 2: Edge case",
            TestCategories = ["HighPriority", "Critical"],
        };

        yield return new TestDataRow((3, "third"))
        {
            IgnoreMessage = "Not yet implemented",
        };
    }
}

DataSourceAttribute

Nota:

DataSource solo está disponible en .NET Framework. En el caso de los proyectos de .NET (Core), use DataRow o DynamicData en su lugar.

DataSourceAttribute Conecta pruebas a orígenes de datos externos, como archivos CSV, archivos XML o bases de datos.

Para obtener información detallada, consulte:

ITestDataSource

La ITestDataSource interfaz le permite crear atributos de origen de datos completamente personalizados. Implemente esta interfaz cuando necesite un comportamiento que no admitan los atributos integrados, como generar datos de prueba basados en variables de entorno, archivos de configuración u otras condiciones en tiempo de ejecución.

Miembros de interfaz

La interfaz define dos métodos:

Método Propósito
GetData(MethodInfo) Devuelve datos de prueba como IEnumerable<object?[]>
GetDisplayName(MethodInfo, object?[]?) Devuelve el nombre que se muestra de un caso de prueba.

Creación de un atributo de origen de datos personalizado

Para crear un origen de datos personalizado, defina una clase de atributo que herede de Attribute e implemente ITestDataSource:

using System.Globalization;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyDataSourceAttribute : Attribute, ITestDataSource
{
    public IEnumerable<object?[]> GetData(MethodInfo methodInfo)
    {
        // Return test data based on your custom logic
        yield return [1, "first"];
        yield return [2, "second"];
        yield return [3, "third"];
    }

    public string? GetDisplayName(MethodInfo methodInfo, object?[]? data)
    {
        return data is null
            ? null
            : string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, string.Join(",", data));
    }
}

[TestClass]
public class CustomDataSourceExample
{
    [TestMethod]
    [MyDataSource]
    public void TestWithCustomDataSource(int value, string name)
    {
        Assert.IsTrue(value > 0);
        Assert.IsNotNull(name);
    }
}

Ejemplo real: datos de prueba basados en entornos

En este ejemplo se muestra un atributo personalizado que genera datos de prueba basados en marcos de destino, filtrado en función del sistema operativo:

using System.Globalization;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public class TargetFrameworkDataAttribute : Attribute, ITestDataSource
{
    private readonly string[] _frameworks;

    public TargetFrameworkDataAttribute(params string[] frameworks)
    {
        _frameworks = frameworks;
    }

    public IEnumerable<object?[]> GetData(MethodInfo methodInfo)
    {
        bool isWindows = OperatingSystem.IsWindows();

        foreach (string framework in _frameworks)
        {
            // Skip .NET Framework on non-Windows platforms
            if (!isWindows && framework.StartsWith("net4", StringComparison.Ordinal))
            {
                continue;
            }

            yield return [framework];
        }
    }

    public string? GetDisplayName(MethodInfo methodInfo, object?[]? data)
    {
        return data is null
            ? null
            : string.Format(CultureInfo.CurrentCulture, "{0} ({1})", methodInfo.Name, data[0]);
    }
}

[TestClass]
public class CrossPlatformTests
{
    [TestMethod]
    [TargetFrameworkData("net48", "net8.0", "net9.0")]
    public void TestOnMultipleFrameworks(string targetFramework)
    {
        // Test runs once per applicable framework
        Assert.IsNotNull(targetFramework);
    }
}

Sugerencia

Para casos sencillos en los que solo tiene que personalizar nombres para mostrar o agregar metadatos, considere la posibilidad de usar TestDataRow<T> con DynamicData en lugar de implementar ITestDataSource. Reserve implementaciones personalizadas ITestDataSource para escenarios que requieran lógica de generación de datos compleja o filtrado dinámico.

Estrategia de despliegue

Los atributos de prueba controlados por datos admiten la TestDataSourceUnfoldingStrategy propiedad , que controla cómo aparecen los casos de prueba en el Explorador de pruebas y los resultados de TRX. Esta propiedad también determina si puede ejecutar casos de prueba individuales de forma independiente.

Estrategias disponibles

Estrategia Comportamiento
Auto (valor predeterminado) MSTest determina la mejor estrategia de desarrollo.
Unfold Todos los casos de prueba se expanden y se muestran individualmente
Fold Todos los casos de prueba se agrupan en un único nodo de prueba.

Cuándo cambiar la estrategia

En la mayoría de los escenarios, el comportamiento predeterminado Auto proporciona el mejor equilibrio. Considere la posibilidad de cambiar esta configuración solo cuando tenga requisitos específicos:

  • Orígenes de datos no deterministas
  • Limitaciones conocidas o errores en MSTest
  • Problemas de rendimiento con muchos casos de prueba

Ejemplo de uso

[TestClass]
public class UnfoldingExample
{
    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Unfold)] // That's the default behavior
    [DataRow(1)]
    [DataRow(2)]
    [DataRow(3)]
    public void TestMethodWithUnfolding(int value, string text)
    {
        // Each test case appears individually in Test Explorer
    }

    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)]
    [DynamicData(nameof(GetData))]
    public void TestMethodWithFolding(int value, string text)
    {
        // All test cases appear as a single collapsed node
    }

    public static IEnumerable<(int, string)> GetData()
    {
        yield return (1, "one");
        yield return (2, "two");
        yield return (3, "three");
    }
}

procedimientos recomendados

  1. Elija el atributo correcto: use DataRow para datos simples y insertados. Se usa DynamicData para datos complejos o calculados.

  2. Asigne un nombre a los casos de prueba: use DisplayName para facilitar la identificación de los errores de prueba.

  3. Mantener los orígenes de datos cerca: defina orígenes de datos en la misma clase cuando sea posible para mejorar la capacidad de mantenimiento.

  4. Usar datos significativos: elija los datos de prueba que ejercen casos perimetrales y condiciones de límite.

  5. Considere la posibilidad de realizar pruebas combinatorias: para probar combinaciones de parámetros, use el paquete Combinatorial.MSTest .

Consulte también