Testování řízené daty v MSTestu

Testování řízené daty umožňuje spustit stejnou testovací metodu s více sadami vstupních dat. Místo psaní samostatných testovacích metod pro každý testovací případ definujte logiku testu jednou a zadejte různé vstupy prostřednictvím atributů nebo externích zdrojů dat.

Přehled

MSTest poskytuje několik atributů pro testování řízené daty:

Vlastnost Případ použití Nejvhodnější pro
DataRow Vestavěná testovací data Jednoduché, statické testovací případy
DynamicData Data z metod, vlastností nebo polí Složitá nebo vypočítaná testovací data
DataSource Externí datové soubory nebo databáze Starší scénáře s externími zdroji dat

MSTest také poskytuje následující typy pro rozšíření scénářů řízených daty:

  • TestDataRow<T>: Návratový typ pro ITestDataSource implementace (včetně DynamicData) přidává podporu metadat, jako jsou zobrazované názvy, kategorie a ignorování zpráv do jednotlivých testovacích případů.
  • ITestDataSource: Rozhraní, které můžete implementovat pro vlastní atribut a vytvořit plně vlastní atributy zdroje dat.

Návod

Pro kombinatorické testování (testování všech kombinací více sad parametrů) použijte opensourcový balíček Combinatorial.MSTest NuGet. Tento komunitní balíček je k dispozici na GitHubu , ale microsoft ho neudržuje.

DataRowAttribute

Umožňuje DataRowAttribute spustit stejnou testovací metodu s několika různými vstupy. Použijte jeden nebo více DataRow atributů na testovací metodu, která je označená TestMethodAttribute.

Počet a typy argumentů musí přesně odpovídat podpisu testovací metody.

Návod

Související analyzátory:

  • MSTEST0014 ověří, že DataRow argumenty odpovídají podpisu testovací metody.
  • MSTEST0042 zjistí duplicitní DataRow položky, které by spustily stejný testovací případ vícekrát.

Základní použití

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

Podporované typy argumentů

DataRow podporuje různé typy argumentů, včetně primitiv, řetězců, polí a hodnot 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
    }
}

Poznámka:

Od verze MSTest v3.10 používejte DateOnly a TimeOnly jako argumenty DataRow a v parametrizovaných testech DynamicData.

Použití parametrů pro argumenty proměnných

Pomocí klíčového params slova můžete přijmout proměnný počet argumentů:

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

Obecné testovací metody

Počínaje verzí MSTest v3.8 TestMethod může mít parametry typu. Rámec odvodí argumenty typu z hodnot DataRow nebo DynamicData:

[TestMethod]
[DataRow(42)]
[DataRow("alpha")]
public void Value_RoundTrips<T>(T value)
{
    Assert.AreEqual(value, value);
}

Vlastní zobrazované názvy

DisplayName Nastavte vlastnost tak, aby se přizpůsobila zobrazení testovacích případů v Průzkumníku testů:

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

Návod

Pokud chcete mít větší kontrolu nad testovacími metadaty, zvažte použití testDataRow<T> s DynamicData. TestDataRow<T> podporuje zobrazování názvů spolu s kategoriemi testů a ignorované zprávy pro jednotlivé testovací případy.

Ignorování konkrétních testovacích případů

Počínaje verzí MSTest v3.8 pomocí IgnoreMessage vlastnosti přeskočte konkrétní datové řádky:

[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

Umožňuje DynamicDataAttribute poskytovat testovací data z metod, vlastností nebo polí. Tento atribut použijte, pokud jsou testovací data složitá, dynamicky vypočítaná nebo příliš podrobná pro vložené DataRow atributy.

Podporované typy zdrojů dat

Zdroj dat může vrátit libovolný IEnumerable<T>, u kterého T je jedním z typů uvedených v následující tabulce. Jakákoli kolekce implementující IEnumerable<T> funguje, včetně List<T>, polí jako T[], nebo vlastních typů kolekcí. Vyberte si podle svých potřeb:

Návratový typ Typová bezpečnost Podpora metadat Nejvhodnější pro
ValueTuple (například (int, string)) Čas kompilace Ne Většina scénářů – jednoduchá syntaxe s úplnou kontrolou typů
Tuple<...> Čas kompilace Ne Když nemůžete použít ValueTuple
TestDataRow<T> Čas kompilace Ano Testovací případy vyžadující zobrazované názvy, kategorie nebo ignorování zpráv
object[] Pouze modul runtime Ne Starší verze kódu – vyhněte se novým testům

Návod

U nových metod testovacích dat použijte ValueTuple pro jednoduché případy nebo TestDataRow<T> v případě, že potřebujete metadata. Vyhněte se object[], protože chybí kontrola typů během kompilace a může způsobit chyby během spuštění z neshod typů.

Zdroje dat

Zdrojem dat může být metoda nebo vlastnost a počínaje msTest v3.11 pole. Tyto zdroje jsou zaměnitelné, takže zvolte podle svých preferencí:

Poznámka:

Počínaje verzí MSTest v3.8 DynamicData automaticky rozpozná typ zdroje dat. Nemusíte explicitně zadávat DynamicDataSourceType .

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

Poznámka:

Metody, vlastnosti a pole zdroje dat musí být public static a vracet IEnumerable<T> podporovaného typu.

Poznámka:

Počínaje verzí MSTest v3.11 může být zdrojem dat pole. Starší verze podporují metody a vlastnosti.

Návod

Související analyzátor: MSTEST0018 ověří, že zdroj dat existuje, je přístupný a má správný podpis.

Zdroj dat z jiné třídy

Zadejte jinou třídu pomocí parametru typu:

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

Parametrizované DynamicData zdrojové metody

Od verze MSTest 3.10 předejte hodnoty metodě zdroje DynamicData pomocí vlastnosti Arguments:

[TestMethod]
[DynamicData(nameof(GetValues), Arguments = new object[] { true })]
public void Value_IsPositive(int value, bool expected) => Assert.AreEqual(expected, value > 0);

public static IEnumerable<(int Value, bool Expected)> GetValues(bool includeZero) =>
    includeZero ? [(1, true), (0, false)] : [(1, true)];

Vlastní zobrazované názvy

Přizpůsobte zobrazované názvy testovacích DynamicDataDisplayName případů pomocí vlastnosti:

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]}'";
    }
}

Poznámka:

Metoda zobrazovaného názvu musí být public static, vrátit stringa přijmout dva parametry: MethodInfo a object[].

Návod

Pro jednodušší přístup k vlastním zobrazovaným názvům zvažte použití TestDataRow<T> s jeho DisplayName vlastností místo samostatné metody.

Ignorování všech testovacích případů ze zdroje dat

Počínaje verzí MSTest v3.8 použijte IgnoreMessage ke přeskočení všech testovacích případů:

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

Návod

Chcete-li ignorovat jednotlivé testovací případy, použijte TestDataRow<T> s jeho IgnoreMessage vlastností. Viz část TestDataRow<T> .

TestDataRow

Třída TestDataRow<T> poskytuje vylepšenou kontrolu nad testovacími daty v testech řízených daty. Použijte IEnumerable<T> typu TestDataRow<T> jako návratový typ zdroje dat pro zadání:

Poznámka:

MSTest byl představen TestDataRow<T> ve verzi 3.8.

  • Vlastní zobrazované názvy: Nastavte jedinečný zobrazovaný název podle testovacího DisplayName případu pomocí vlastnosti.
  • Kategorie testů: Připojte metadata k jednotlivým testovacím případům pomocí TestCategories vlastnosti.
  • Ignorovat zprávy: Přeskočte konkrétní testovací případy a uveďte důvody pomocí vlastnosti IgnoreMessage.
  • Typově bezpečná data: Pro testovací data silného typu používejte obecné typy.

Základní použití

[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

Poznámka:

DataSource je k dispozici pouze v rozhraní .NET Framework. Pro projekty .NET (Core) použijte DataRow nebo DynamicData místo něj.

Testy DataSourceAttribute se připojují k externím zdrojům dat, jako jsou soubory CSV, soubory XML nebo databáze.

Podrobné informace najdete tady:

ITestDataSource

Rozhraní ITestDataSource umožňuje vytvářet zcela vlastní atributy zdroje dat. Toto rozhraní implementujte, pokud potřebujete chování, které předdefinované atributy nepodporují, například generování testovacích dat na základě proměnných prostředí, konfiguračních souborů nebo jiných podmínek modulu runtime.

Členy rozhraní

Rozhraní definuje dvě metody:

Metoda Účel
GetData(MethodInfo) Vrátí testovací data jako IEnumerable<object?[]>
GetDisplayName(MethodInfo, object?[]?) Vrátí zobrazovaný název testovacího případu.

Vytvoření vlastního atributu zdroje dat

Pokud chcete vytvořit vlastní zdroj dat, definujte třídu atributů, která dědí z Attribute a implementuje 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);
    }
}

Příklad z reálného světa: Testovací data založená na prostředí

Tento příklad ukazuje vlastní atribut, který generuje testovací data na základě cílových architektur a filtrování na základě operačního systému:

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

Návod

V jednoduchých případech, kdy stačí přizpůsobit zobrazované názvy nebo přidat metadata, zvažte použití TestDataRow<T> místo DynamicData implementace ITestDataSource. Vyhraďte si vlastní ITestDataSource implementace pro scénáře, které vyžadují dynamické filtrování nebo komplexní logiku generování dat.

Strategie odhalování

Atributy testů řízené daty podporují TestDataSourceUnfoldingStrategy vlastnost, která řídí, jak se testovací případy zobrazují v Průzkumníku testů a výsledcích TRX. Tato vlastnost také určuje, zda můžete spustit jednotlivé testovací případy nezávisle.

Fáze zjišťování a provádění

MSTest zpracovává testy řízené daty ve dvou různých fázích:

  • Fáze zjišťování: MSTest vyhodnotí všechny atributy zdroje dat (DataRow, DynamicData, ITestDataSource) k určení seznamu testovacích případů. K tomuto vyhodnocení dochází před spuštěním všech běžných háků životního cyklu MSTest—AssemblyInitialize, ClassInitialize a další metody nastavení se ještě nespustily.
  • Fáze provádění: MSTest spustí normální životní cyklus (inicializace sestavení, inicializace třídy, inicializace testů, testovací metoda, vyčištění) a spustí každý testovací případ s jejími daty.

Vzhledem k tomu, že se zdroje dat vyhodnocují během zjišťování, kód generování dat nemůže spoléhat na žádný stav nastavený AssemblyInitialize nebo ClassInitialize. Pokud váš zdroj dat závisí na logice nastavení (například čtení z připojení k databázi inicializované v ClassInitialize), vyhodnocení zdroje dat se během zjišťování nezdaří.

Dostupné strategie

Strategie Chování
Auto (výchozí) MSTest určuje nejlepší strategii pro rozvinutí.
Unfold Všechny testovací případy jsou rozbaleny a zobrazeny jednotlivě.
Fold Všechny testovací případy jsou sloučeny do jednoho testovacího uzlu.

Složené vs. rozložené testy

Strategie rozbalení ovlivňuje způsob, jakým jsou výsledky testů hlášeny v Test Exploreru a ve výstupu TRX.

  • Rozbalené testy: Každý datový řádek se zobrazí jako samostatný nezávislý testovací záznam v Průzkumníku testů a TRX. Můžete spouštět, ladit nebo filtrovat jednotlivé testovací případy. Každá položka má svůj vlastní stav pass/fail.
  • Složené testy: Všechny řádky dat se v Průzkumníku testů zobrazí jako jeden testovací uzel. V TRX se zobrazí jedna položka s více výsledky přidruženými k jednomu testovacímu případu. Jednotlivé řádky dat nelze spouštět ani filtrovat nezávisle.

Výjimka při zjišťování způsobuje posouvání

Když MSTest vyhodnotí zdroj dat během zjišťování a vyhodnocení vyvolá výjimku, test se vrátí do přeloženého stavu bez ohledu na nakonfigurovanou strategii rozbalení. Vzhledem k tomu, že architektura nemůže vytvořit výčet jednotlivých testovacích případů, zaregistruje testovací metodu jako jednu (složenou) položku.

V době provádění nástroj MSTest vyhodnotí zdroj dat znovu jako součást normálního životního cyklu. Pokud byla výjimka způsobená chybějícím stavem instalace (například závislostí, kterou ClassInitialize poskytuje), může být zdroj dat při provádění úspěšný, protože nyní proběhly události životního cyklu.

Poznámka:

Při ladění testu, který vyvolává výjimku během zjišťování chyb, se může před spuštěním testu zobrazit výjimka (například NullReferenceException). Tato výjimka pochází z vyhodnocení fáze zjišťování. Stiskněte pokračovat v ladicím programu – test se pak spustí normálně, protože fáze provádění spouští celý životní cyklus MSTest, včetně inicializačních metod. Další podrobnosti najdete v microsoft/testfx#7774.

Kdy změnit strategii

Ve většině scénářů poskytuje výchozí Auto chování nejlepší rovnováhu. Zvažte změnu strategie nasazení, pokud máte specifické požadavky:

  • Použijte Fold , pokud zdroje dat závisí na stavu modulu runtime nebo na logice nastavení, která není během zjišťování dostupná.
  • Používá se Fold pro ne deterministické zdroje dat, které při každém vyhodnocení vrací různé hodnoty.
  • Použijte Fold ke snížení zatížení, pokud je výkon důležitý při práci s velkým počtem testovacích případů.

Příklad využití

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

Osvědčené postupy

  • Zvolte správný atribut: Použijte DataRow pro jednoduchá vložená data. Používá se DynamicData pro složitá nebo vypočítaná data.
  • Pojmenujte testovací případy: Umožňuje DisplayName usnadnit identifikaci selhání testů.
  • Zachovat zdroje dat blízko: Pokud je to možné, definujte zdroje dat ve stejné třídě, aby bylo možné lépe udržovat.
  • Používejte smysluplná data: Zvolte testovací data, která testují mezní případy a okrajové podmínky.
  • Zvažte kombinatorické testování: Pro kombinace parametrů testování použijte balíček Combinatorial.MSTest .

Viz také