Adatvezérelt tesztelés az MSTestben

Az adatvezérelt tesztelés lehetővé teszi ugyanazt a tesztmetódus futtatását több bemeneti adatkészlettel. Ahelyett, hogy külön tesztelési módszereket írnál minden egyes tesztesethez, definiáld egyszer a teszt logikáját, és adjon meg különböző bemeneteket attribútumokon vagy külső adatforrásokon keresztül.

Áttekintés

Az MSTest számos attribútumot biztosít az adatvezérelt teszteléshez:

Attribute Felhasználási eset A következőkre alkalmas
DataRow Beágyazott tesztadatok Egyszerű, statikus tesztesetek
DynamicData Metódusokból, tulajdonságokból vagy mezőkből származó adatok Összetett vagy számított tesztadatok
DataSource Külső adatfájlok vagy adatbázisok Régi forgatókönyvek külső adatforrásokkal

Az MSTest az alábbi típusokat is biztosítja az adatvezérelt forgatókönyvek kibővítéséhez:

  • TestDataRow<T>: Olyan implementációk visszatérési ITestDataSource típusa (beleértve a következőket is DynamicData), amelyek metaadat-támogatást adnak hozzá, például megjelenítendő neveket, kategóriákat és figyelmen kívül hagyják az egyes tesztesetekhez tartozó üzeneteket.
  • ITestDataSource: Egy egyéni attribútumon implementálható felület, amely teljesen egyéni adatforrásattribútumokat hoz létre.

Jótanács

A kombinatorikus teszteléshez (több paraméterkészlet összes kombinációjának teszteléséhez) használja a nyílt forráskódú Combinatorial.MSTest NuGet-csomagot. Ez a közösség által fenntartott csomag elérhető a GitHubon , de a Microsoft nem tartja karban.

DataRowAttribute

Ez DataRowAttribute lehetővé teszi ugyanazt a tesztmetódus futtatását több különböző bemenettel. Alkalmazzon egy vagy több DataRow attribútumot egy tesztmetódusra, amely a TestMethodAttribute-vel van díszítve.

Az argumentumok számának és típusának pontosan meg kell egyeznie a vizsgálati módszer aláírásával.

Jótanács

Kapcsolódó elemzők:

  • MSTEST0014 ellenőrzi, hogy az argumentumok egyeznek-e DataRow a tesztmetódus aláírásával.
  • MSTEST0042 ismétlődő DataRow bejegyzéseket észlel, amelyek ugyanazt a tesztesetet többször futtatnák.

Alapszintű használat

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

Támogatott argumentumtípusok

DataRow Különböző argumentumtípusokat támogat, például primitíveket, sztringeket, tömböket és null értékeket:

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

Paramok használata változóargumentumokhoz

params A kulcsszóval változó számú argumentumot fogadhat el:

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

Egyéni megjelenítési nevek

Állítsa be a tulajdonságot a DisplayName tesztesetek megjelenésének testreszabásához a Test Explorerben:

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

Jótanács

A tesztelési metaadatok további szabályozásához fontolja meg a TestDataRow<T> használatát.DynamicData TestDataRow<T> Támogatja a megjelenítendő neveket a tesztkategóriákkal együtt, és figyelmen kívül hagyja az egyes tesztesetekhez tartozó üzeneteket.

Adott tesztelési esetek figyelmen kívül hagyása

Az MSTest 3.8-s verziótól kezdve a IgnoreMessage tulajdonsággal kihagyhat bizonyos adatsorokat:

[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

Ez DynamicDataAttribute lehetővé teszi a metódusok, tulajdonságok vagy mezők tesztelési adatainak megadását. Ezt az attribútumot akkor használja, ha a tesztadatok összetettek, dinamikusan kiszámítottak vagy túl részletesek a beágyazott DataRow attribútumokhoz.

Támogatott adatforrástípusok

Az adatforrás visszaadhat bármilyen IEnumerable<T>, ahol T az alábbi táblázatban felsorolt típusok egyike. Bármely gyűjtemény, amely implementálja a IEnumerable<T>-t, működik, beleértve a List<T>, tömböket, mint például a T[], vagy egyéni gyűjteménytípusokat. Válasszon az igényei alapján:

Visszatérési típus Típusbiztonság Metaadatok támogatása A következőkre alkalmas
ValueTuple (például: (int, string)) Fordítási idő Nem A legtöbb forgatókönyv – egyszerű szintaxis teljes típusellenőrzéssel
Tuple<...> Fordítási idő Nem Ha nem tudja használni a ValueTuple
TestDataRow<T> Fordítási idő Igen Megjelenítendő neveket, kategóriákat vagy üzenetek figyelmen kívül hagyását igénylő esetek tesztelése
object[] Csak futásidejű környezet Nem Régi kód – ne használjon új teszteket

Jótanács

Új tesztadat-módszerek esetén használja az ValueTuple egyszerű esetekhez, vagy az TestDataRow<T> amikor metaadatra van szükség. Kerülje object[], mert hiányzik belőle a fordítási idő alatti típusellenőrzés, és a típuseltérések futásidejű hibákat okozhatnak.

Adatforrások

Az adatforrás lehet metódus, tulajdonság vagy mező. Mindhárom felcserélhető – válassza a beállítások alapján:

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

Megjegyzés:

Az adatforrás metódusainak, tulajdonságainak és mezőinek támogatott típusú public static-nak kell lenniük, és egy IEnumerable<T>-t kell visszaadniuk.

Jótanács

Kapcsolódó elemző: MSTEST0018 ellenőrzi, hogy az adatforrás létezik-e, elérhető-e, és rendelkezik-e a megfelelő aláírással.

Adatforrás egy másik osztályból

Adjon meg egy másik osztályt a típusparaméterrel:

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

Egyéni megjelenítési nevek

A tesztesetek megjelenítendő neveinek testreszabása a DynamicDataDisplayName tulajdonság használatával:

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

Megjegyzés:

A megjelenítési név metódusának egy értéket kell visszaadniapublic staticstring, és két paramétert kell elfogadnia: MethodInfo ésobject[].

Jótanács

Az egyéni megjelenítési nevek egyszerűbb megközelítéséhez fontolja meg a TestDataRow<T>DisplayName tulajdonságának használatát egy külön metódus helyett.

Az adatforrás összes tesztesetének figyelmen kívül hagyása

Az MSTest v3.8-as verziótól kezdve a IgnoreMessage minden teszteset kihagyására használható.

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

Jótanács

Az egyedi tesztesetek figyelmen kívül hagyásához használja a TestDataRow<T> szolgáltatást a IgnoreMessage tulajdonsággal. Lásd a TestDataRow<T> szakaszt.

TestDataRow

Az TestDataRow<T> osztály továbbfejlesztett vezérlést biztosít a tesztadatok felett az adatvezérelt tesztekben. A IEnumerable<T> és a TestDataRow<T> alkalmazása adattípusú visszatérési értékként az alábbiak meghatározásához:

  • Egyéni megjelenítési nevek: Tesztesetenként egyedi megjelenítendő név beállítása a DisplayName tulajdonság használatával.
  • Tesztkategóriák: Metaadatok csatolása a tulajdonságot használó egyes tesztelési TestCategories esetekhez.
  • Üzenetek figyelmen kívül hagyása: Adott teszteseteket kihagyhat a IgnoreMessage tulajdonság használatával kapcsolatos indokokkal.
  • Típusbiztos adatok: Általános adatok használata erősen gépelt tesztadatokhoz.

Alapszintű használat

[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

Megjegyzés:

DataSource csak a .NET-keretrendszerben érhető el. .NET (Core) projektekhez használja a DataRow vagy a DynamicData helyette.

A DataSourceAttribute teszteket külső adatforrásokhoz, például CSV-fájlokhoz, XML-fájlokhoz vagy adatbázisokhoz csatlakoztatja.

Részletes információkért lásd:

ITestDataSource

Az ITestDataSource interfész lehetővé teszi teljesen egyéni adatforrás-attribútumok létrehozását. Implementálja ezt az felületet, ha olyan viselkedésre van szüksége, amelyet a beépített attribútumok nem támogatnak, például tesztadatokat generál környezeti változók, konfigurációs fájlok vagy egyéb futtatókörnyezeti feltételek alapján.

Interfésztagok

Az interfész két módszert határoz meg:

Metódus Cél
GetData(MethodInfo) Tesztadatokat ad vissza IEnumerable<object?[]>
GetDisplayName(MethodInfo, object?[]?) Egy teszteset megjelenítendő nevét adja vissza

Egyéni adatforrásattribútum létrehozása

Egyéni adatforrás létrehozásához definiáljon egy attribútumosztályt, amely a Attribute osztályt örökli, és a ITestDataSource interfészt implementálja.

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

Valós példa: Környezetalapú tesztadatok

Ez a példa egy egyéni attribútumot mutat be, amely tesztadatokat hoz létre a cél-keretrendszerek alapján, az operációs rendszer alapján történő szűréssel:

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

Jótanács

Olyan egyszerű esetekben, amikor csak testre kell szabnia a megjelenített neveket, vagy metaadatokat kell hozzáadnia, fontolja meg a TestDataRow<T>DynamicData használatát a implementálás ITestDataSourcehelyett. Használjon egyéni ITestDataSource implementációkat olyan forgatókönyvekhez, amelyek dinamikus szűrést vagy összetett adatlétrehozási logikát igényelnek.

A stratégia kibontása

Az adatvezérelt tesztattribútumok támogatják a TestDataSourceUnfoldingStrategy tulajdonságot, amely szabályozza, hogy a tesztesetek hogyan jelenjenek meg a Test Explorerben és a TRX-eredményekben. Ez a tulajdonság azt is meghatározza, hogy önállóan futtathat-e egyes teszteseteket.

Felderítési és végrehajtási fázisok

Az MSTest két különböző fázisban dolgozza fel az adatvezérelt teszteket:

  • Felderítési fázis: Az MSTest kiértékeli az összes adatforrásattribútumot (DataRow, DynamicData, ) ITestDataSourcea tesztelési esetek listájának meghatározásához. Ez a kiértékelés a normál MSTest életciklus-horgok futtatása előtt történik–AssemblyInitializeClassInitialize és más beállítási módszerek még nem hajthatóak végre.
  • Végrehajtási fázis: Az MSTest a normál életciklust futtatja (szerelvény inicializálása, osztály inicializálása, tesztelési inicializálás, tesztelési módszer, törlés), és minden tesztesetet végrehajt az adataival.

Mivel az adatforrások kiértékelése a felderítés során történik, az adatgeneráló kód nem támaszkodhat a `AssemblyInitialize` vagy a `ClassInitialize` által beállított állapotokra. Ha az adatforrás a beállítási logikától függ (például egy inicializált ClassInitializeadatbázis-kapcsolatból való olvasás), az adatforrás kiértékelése sikertelen lesz a felderítés során.

Elérhető stratégiák

Stratégia Magatartás
Auto (alapértelmezett) Az MSTest határozza meg a legjobb kibontakozási stratégiát.
Unfold Minden teszteset ki van bontva, és egyenként jelenik meg.
Fold Minden teszteset egyetlen tesztcsomópontba van összecsukva.

Összecsukott és ki nem bontott tesztek

A kibontási stratégia hatással van a teszteredmények jelentésére a Test Explorerben és a TRX-kimenetben:

  • Kibontott tesztek: Minden adatsor külön, független tesztbejegyzésként jelenik meg a Test Explorerben és a TRX-ben. Az egyes teszteseteket futtathatja, hibakeresésre vagy szűrésre is használhatja. Minden bejegyzés saját pass/fail állapotú.
  • Összecsukott tesztek: Minden adatsor egyetlen tesztcsomópontként jelenik meg a Test Explorerben. Egy bejegyzés jelenik meg a TRX-ben, amely több eredményt is társít az adott tesztesethez. Az egyes adatsorok egymástól függetlenül nem futtathatók és nem szűrhetők.

Kivétel a felderítés során összeomlást okoz

Ha az MSTest kiértékel egy adatforrást a felderítés során, és a kiértékelés kivételt eredményez, a teszt a konfigurált bontási stratégiától függetlenül visszaáll a hajtogatott állapotra. Mivel a keretrendszer nem tudja felsorolni az egyes teszteseteket, a tesztmetódus egyetlen (összecsukott) bejegyzésként van regisztrálva.

A végrehajtáskor az MSTest újra kiértékeli az adatforrást a normál életciklus részeként. Ha a kivételt hiányzó beállítási állapot (például egy ClassInitialize függőség) okozta, az adatforrás sikeres lehet a végrehajtás során, mert az életciklus-horgok már futnak.

Megjegyzés:

A felderítés során felmerülő teszt hibakeresése esetén kivétel (például a NullReferenceException) jelenhet meg a teszt megkezdése előtt. Ez a kivétel a felderítési fázis kiértékeléséből származik. Nyomja le a Continue billentyűt a hibakeresőben – a teszt ezután normálisan fut, mert a végrehajtási fázis a teljes MSTest-életciklust futtatja, beleértve az inicializálási módszereket is. További részletekért lásd: microsoft/testfx#7774.

Mikor kell módosítani a stratégiát?

A legtöbb forgatókönyv esetében az alapértelmezett Auto viselkedés biztosítja a legjobb egyensúlyt. Ha konkrét követelményekkel rendelkezik, fontolja meg a kibontási stratégia módosítását:

  • Akkor használja Fold , ha az adatforrások a futtatókörnyezet állapotától vagy a felderítés során nem elérhető beállítási logikától függnek.
  • Olyan nem determinisztikus adatforrásokhoz használható Fold , amelyek különböző értékeket adnak vissza az egyes értékelésekhez.
  • Használja a Fold-t a többletterhelés csökkentésére, ha a nagy számú teszteset teljesítménye aggodalomra ad okot.

Példa használatra

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

Ajánlott eljárások

  • Válassza ki a megfelelő attribútumot: Egyszerű, beágyazott adatokhoz használható DataRow . Összetett vagy számított adatokhoz használható DynamicData .
  • Nevezze el a teszteseteket: A tesztelési hibák könnyebb azonosítására használható DisplayName .
  • Az adatforrások közel tartása: Az adatforrások definiálása ugyanabban az osztályban, ha lehetséges, a jobb karbantarthatóság érdekében.
  • Hasznos adatok használata: Válasszon ki olyan tesztadatokat, amelyek éles eseteket és határfeltételeket gyakorolnak.
  • Fontolja meg a kombinatorikus tesztelést: A paraméterek kombinációinak teszteléséhez használja a Combinatorial.MSTest csomagot.

Lásd még