Delen via


Gegevensgestuurde tests in MSTest

Met gegevensgestuurde tests kunt u dezelfde testmethode uitvoeren met meerdere sets invoergegevens. In plaats van afzonderlijke testmethoden te schrijven voor elke testcase, definieert u uw testlogica eenmaal en levert u verschillende invoer via kenmerken of externe gegevensbronnen.

Overzicht

MSTest biedt verschillende kenmerken voor gegevensgestuurde tests:

Attribute Gebruiksituatie Ideaal voor
DataRow Inline-testgegevens Eenvoudige, statische testcases
DynamicData Gegevens uit methoden, eigenschappen of velden Complexe of berekende testgegevens
DataSource Externe gegevensbestanden of -databases Verouderde scenario's met externe gegevensbronnen

MSTest biedt ook de volgende typen voor het uitbreiden van gegevensgestuurde scenario's:

  • TestDataRow<T>: Een retourtype voor ITestDataSource implementaties (inclusief DynamicData) waarmee metagegevensondersteuning wordt toegevoegd, zoals weergavenamen, categorieën en het negeren van berichten voor afzonderlijke testcases.
  • ITestDataSource: Een interface die u op een aangepast kenmerk kunt implementeren om volledig aangepaste gegevensbronkenmerken te maken.

Aanbeveling

Gebruik het opensource Combinatorial.MSTest NuGet-pakket voor combinaties (alle combinaties van meerdere parametersets testen). Dit door de community onderhouden pakket is beschikbaar op GitHub , maar wordt niet onderhouden door Microsoft.

DataRowAttribute

Hiermee DataRowAttribute kunt u dezelfde testmethode uitvoeren met meerdere verschillende invoerwaarden. Pas een of meerdere DataRow kenmerken toe op een testmethode en combineer deze met de TestMethodAttribute.

Het aantal en de typen argumenten moeten exact overeenkomen met de handtekening van de testmethode.

Aanbeveling

Gerelateerde analyses:

  • MSTEST0014 valideert dat DataRow argumenten overeenkomen met de handtekening van de testmethode.
  • MSTEST0042 detecteert dubbele DataRow vermeldingen die dezelfde testcase meerdere keren zouden uitvoeren.

Basaal gebruik

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

Ondersteunde argumenttypen

DataRow ondersteunt verschillende argumenttypen, waaronder primitieven, tekenreeksen, matrices en null-waarden:

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

Parameters gebruiken voor variabele argumenten

Gebruik het params trefwoord om een variabel aantal argumenten te accepteren:

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

Aangepaste weergavenamen

Stel de DisplayName eigenschap in om aan te passen hoe testcases worden weergegeven in Test Explorer:

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

Aanbeveling

Voor meer controle over testmetagegevens kunt u Overwegen TestDataRow<T> te gebruiken met DynamicData. TestDataRow<T> ondersteunt weergavenamen, testcategorieën en negeerberichten voor afzonderlijke testcases.

Specifieke testcases negeren

Vanaf MSTest v3.8 gebruikt u de IgnoreMessage eigenschap om specifieke gegevensrijen over te slaan:

[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

Hiermee DynamicDataAttribute kunt u testgegevens opgeven uit methoden, eigenschappen of velden. Gebruik dit kenmerk wanneer testgegevens complex zijn, dynamisch worden berekend of te uitgebreid zijn voor inlinekenmerken DataRow .

Ondersteunde gegevensbrontypen

De gegevensbron kan elk IEnumerable<T> retourneren waarbij T een van de typen is die in de volgende tabel worden vermeld. Alle verzamelingen die IEnumerable<T> implementeren, waaronder List<T>, arrays zoals T[], of aangepaste verzameltypen. Kies op basis van uw behoeften:

Resultaattype Typeveiligheid Ondersteuning voor metagegevens Ideaal voor
ValueTuple (bijvoorbeeld, (int, string)) Compileertijd Nee. De meeste scenario's: eenvoudige syntaxis met volledige typecontrole
Tuple<...> Compileertijd Nee. Wanneer u niet kunt gebruiken ValueTuple
TestDataRow<T> Compileertijd Yes Testcases die weergavenamen, categorieën of negeren van berichten nodig hebben
object[] Alleen de runtime Nee. Verouderde code: vermijden voor nieuwe tests

Aanbeveling

Gebruik voor nieuwe testgegevensmethoden ValueTuple voor eenvoudige gevallen of TestDataRow<T> wanneer u metagegevens nodig hebt. Vermijd object[] omdat het geen controle van het type tijdens compileertijd biedt en runtimefouten kan veroorzaken vanwege typeverschillen.

Gegevensbronnen

De gegevensbron kan een methode, eigenschap of veld zijn. Alle drie zijn uitwisselbaar: kies op basis van uw voorkeur:

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

Opmerking

Methoden, eigenschappen en velden van gegevensbronnen moeten public static zijn en een IEnumerable<T> van een ondersteund type retourneren.

Aanbeveling

Gerelateerde analyse: MSTEST0018 valideert of de gegevensbron bestaat, toegankelijk is en de juiste handtekening heeft.

Gegevensbron uit een andere klasse

Geef een andere klasse op met behulp van de typeparameter:

public class TestDataProvider
{
    public static IEnumerable<(int, string)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }
}

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

Aangepaste weergavenamen

Weergavenamen van testcases aanpassen met behulp van de DynamicDataDisplayName eigenschap:

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<(int, string)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }

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

Opmerking

De weergavenaammethode moet zijn public static, retourneert een stringen accepteert twee parameters: MethodInfo en object[].

Aanbeveling

Voor een eenvoudigere benadering van aangepaste weergavenamen kunt u Overwegen TestDataRow<T> te gebruiken met de DisplayName eigenschap ervan in plaats van een afzonderlijke methode.

Alle testcases van een gegevensbron negeren

Vanaf MSTest v3.8 kunt u met IgnoreMessage alle testcases overslaan.

[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<(int, string)> GetTestData()
    {
        yield return (1, "first");
        yield return (2, "second");
    }
}

Aanbeveling

Als u afzonderlijke testcases wilt negeren, gebruikt u TestDataRow<T> met de IgnoreMessage eigenschap. Zie de sectie TestDataRow<T> .

TestDataRow

De TestDataRow<T> klasse biedt verbeterde controle over testgegevens in gegevensgestuurde tests. Gebruik IEnumerable<T> als het retourtype van uw gegevensbron TestDataRow<T> om het volgende op te geven:

  • Aangepaste weergavenamen: Stel een unieke weergavenaam per testcase in met behulp van de DisplayName eigenschap.
  • Testcategorieën: voeg metagegevens toe aan afzonderlijke testcases met behulp van de TestCategories eigenschap.
  • Berichten negeren: sla specifieke testcases over met redenen die de IgnoreMessage eigenschap gebruiken.
  • Typeveilige gegevens: gebruik generics voor sterk getypte testgegevens.

Basaal gebruik

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

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

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

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

DataSourceAttribute

Opmerking

DataSource is alleen beschikbaar in .NET Framework. Voor .NET (Core)-projecten gebruikt u DataRow of DynamicData in plaats van.

Hiermee DataSourceAttribute worden tests verbonden met externe gegevensbronnen, zoals CSV-bestanden, XML-bestanden of databases.

Zie voor gedetailleerde informatie:

ITestDataSource

Met de ITestDataSource interface kunt u volledig aangepaste gegevensbronkenmerken maken. Implementeer deze interface wanneer u gedrag nodig hebt dat de ingebouwde kenmerken niet ondersteunen, zoals het genereren van testgegevens op basis van omgevingsvariabelen, configuratiebestanden of andere runtimevoorwaarden.

Interfaceleden

De interface definieert twee methoden:

Methode Purpose
GetData(MethodInfo) Retourneert testgegevens als IEnumerable<object?[]>
GetDisplayName(MethodInfo, object?[]?) Retourneert de weergavenaam voor een testcase

Een aangepast gegevensbronkenmerk maken

Als u een aangepaste gegevensbron wilt maken, definieert u een kenmerkklasse die wordt overgenomen van Attribute en implementeert 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);
    }
}

Praktijkvoorbeeld: testgegevens op basis van omgeving

In dit voorbeeld ziet u een aangepast kenmerk dat testgegevens genereert op basis van doelframeworks, filteren op basis van het besturingssysteem:

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

Aanbeveling

Voor eenvoudige gevallen waarin u alleen weergavenamen moet aanpassen of metagegevens moet toevoegen, kunt u overwegen TestDataRow<T> te gebruiken in DynamicData plaats van te implementeren ITestDataSource. Reserveer aangepaste ITestDataSource implementaties voor scenario's waarvoor dynamische filtering of complexe logica voor het genereren van gegevens is vereist.

Strategie uitvouwen

Gegevensgestuurde testkenmerken ondersteunen de TestDataSourceUnfoldingStrategy eigenschap, waarmee wordt bepaald hoe testcases worden weergegeven in testverkenner en TRX-resultaten. Deze eigenschap bepaalt ook of u afzonderlijke testcases onafhankelijk kunt uitvoeren.

Beschikbare strategieën

Strategie Gedrag
Auto (standaard) MSTest bepaalt het uitvouwen van de beste strategie
Unfold Alle testcases worden uitgevouwen en afzonderlijk weergegeven
Fold Alle testcases worden samengebracht tot één testknooppunt

Wanneer de strategie moet worden gewijzigd

Voor de meeste scenario's biedt het standaardgedrag Auto de beste balans. U kunt deze instelling alleen wijzigen wanneer u specifieke vereisten hebt:

  • Niet-deterministische gegevensbronnen
  • Bekende beperkingen of fouten in MSTest
  • Prestatieproblemen met veel testcases

Voorbeeld van gebruik

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

    [TestMethod(UnfoldingStrategy = TestDataSourceUnfoldingStrategy.Fold)]
    [DataRow(1, "one")]
    [DataRow(2, "two")]
    [DataRow(3, "three")]
    public void TestMethodWithFolding(int value, string text)
    {
        // All test cases appear as a single collapsed node
    }
}

Beste praktijken

  1. Kies het juiste kenmerk: Gebruik DataRow dit voor eenvoudige, inlinegegevens. Gebruiken DynamicData voor complexe of berekende gegevens.

  2. Geef uw testcases een naam: gebruik DisplayName deze om testfouten gemakkelijker te identificeren.

  3. Gegevensbronnen dicht houden: definieer indien mogelijk gegevensbronnen in dezelfde klasse voor betere onderhoudbaarheid.

  4. Gebruik zinvolle gegevens: kies testgegevens die edge-cases en grensvoorwaarden uitvoeren.

  5. Considereer combinatorieel testen: gebruik het Combinatorial.MSTest pakket voor het testen van parametercombinaties.

Zie ook