Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
I test basati sui dati consentono di eseguire lo stesso metodo di test con più set di dati di input. Anziché scrivere metodi di test separati per ogni test case, definire la logica di test una sola volta e fornire input diversi tramite attributi o origini dati esterne.
Informazioni generali
MSTest offre diversi attributi per i test basati sui dati:
| Attribute | Caso d'uso | Ideale per |
|---|---|---|
DataRow |
Dati di test inline | Test case semplici e statici |
DynamicData |
Dati provenienti da metodi, proprietà o campi | Dati di test complessi o calcolati |
TestDataRow<T> |
Dati avanzati con metadati | Casi di test che necessitano di nomi visualizzati o categorie |
DataSource |
File di dati esterni o database | Scenari legacy con fonti di dati esterne |
ITestDataSource |
Attributi dell'origine dati personalizzata | Scenari completamente personalizzati basati sui dati |
Suggerimento
Per i test combinatoriali (test di tutte le combinazioni di più set di parametri), usare il pacchetto NuGet Combinatorial.MSTest open source. Questo pacchetto gestito dalla community è disponibile in GitHub , ma non è gestito da Microsoft.
DataRowAttribute
DataRowAttribute consente di eseguire lo stesso metodo di test con più input diversi. Applicare uno o più DataRow attributi a un metodo di test e combinarlo con .TestMethodAttribute
Il numero e i tipi di argomenti devono corrispondere esattamente alla firma del metodo di test.
Suggerimento
Analizzatori correlati:
-
MSTEST0014 convalida che
DataRowgli argomenti corrispondano alla firma del metodo di test. -
MSTEST0042 rileva voci duplicate
DataRowche potrebbero eseguire lo stesso caso di test più volte.
Utilizzo di base
[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));
}
}
Tipi di argomenti supportati
DataRow supporta vari tipi di argomento, tra cui primitive, stringhe, matrici e valori 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 di params per argomenti variabili
Usare la params parola chiave per accettare un numero variabile di argomenti:
[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);
}
}
Nomi visualizzati personalizzati
Impostare la proprietà DisplayName per personalizzare come appaiono i test case in Esplora 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);
}
}
Suggerimento
Per un maggiore controllo sui metadati di test, provare a usare TestDataRow<T> con DynamicData.
TestDataRow<T> supporta i nomi visualizzati insieme alle categorie di test e l'esclusione dei messaggi per i singoli casi di test.
Ignorare test case specifici
A partire da MSTest v3.8, usare la IgnoreMessage proprietà per ignorare righe di dati specifiche:
[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 Consente di fornire dati di test da metodi, proprietà o campi. Usare questo attributo quando i dati di test sono complessi, calcolati in modo dinamico o troppo dettagliato per gli attributi inline DataRow .
Tipi di origine dati supportati
L'origine dati può restituire qualsiasi IEnumerable<T> dove T è uno dei tipi elencati nella tabella seguente. Qualsiasi raccolta che implementa IEnumerable<T> funziona, inclusi List<T>, matrici come T[]o tipi di raccolta personalizzati. Scegliere in base alle proprie esigenze:
| Tipo di ritorno | Sicurezza dei tipi | Supporto dei metadati | Ideale per |
|---|---|---|---|
ValueTuple (ad esempio, (int, string)) |
Fase di compilazione | NO | La maggior parte degli scenari: sintassi semplice con controllo completo dei tipi |
Tuple<...> |
Fase di compilazione | NO | Quando non è possibile usare ValueTuple |
TestDataRow<T> |
Tempo di compilazione | Yes | Casi di test che richiedono nomi di visualizzazione, categorie o messaggi da ignorare |
object[] |
Solo runtime | NO | Codice legacy - da evitare per nuovi test |
Suggerimento
Per i nuovi metodi di dati di test, usare ValueTuple per casi semplici o TestDataRow<T> quando sono necessari metadati. Evitare object[] perché manca il controllo dei tipi in fase di compilazione e può causare errori di runtime da mancate corrispondenze di tipo.
Origini dati
L'origine dati può essere un metodo, una proprietà o un campo. Tutti e tre sono intercambiabili: scegliere in base alle proprie preferenze:
[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);
}
}
Annotazioni
I metodi, le proprietà e i campi dell'origine dati devono essere public static e restituire un IEnumerable<T> oggetto di un tipo supportato.
Suggerimento
Analizzatore correlato: MSTEST0018 verifica che l'origine dati esista, sia accessibile e abbia la firma corretta.
Fonte dati di una classe diversa
Specificare una classe diversa usando il parametro di tipo :
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);
}
}
Nomi visualizzati personalizzati
Personalizzare i nomi visualizzati del test case usando la DynamicDataDisplayName proprietà :
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]}'";
}
}
Annotazioni
Il metodo del nome visualizzato deve essere public static, restituire un stringe accettare due parametri: MethodInfo e object[].
Suggerimento
Per un approccio più semplice ai nomi visualizzati personalizzati, è consigliabile usare TestDataRow<T> con la relativa DisplayName proprietà anziché un metodo separato.
Ignorare tutti i casi di test da un'origine dati
A partire da MSTest v3.8, usare IgnoreMessage per ignorare tutti i test case:
[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" };
}
}
Suggerimento
Per ignorare i singoli test case, usare TestDataRow<T> con la relativa IgnoreMessage proprietà . Vedere la sezione TestDataRow<T> .
TestDataRow
La TestDataRow<T> classe fornisce un controllo avanzato sui dati di test nei test basati sui dati. Usare IEnumerable<TestDataRow<T>> come tipo di ritorno dell'origine dati per specificare:
- Nomi visualizzati personalizzati: impostare un nome visualizzato univoco per ogni test case
- Categorie di test: Allegare metadati a singoli test case
- Ignora messaggi: Salta test case specifici fornendo motivazioni
- Dati sicuri rispetto ai tipi: usare generics per i dati di test fortemente tipizzati
Utilizzo di base
[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
Annotazioni
DataSource è disponibile solo in .NET Framework. Per i progetti .NET (Core), usare DataRow o DynamicData .
DataSourceAttribute Connette i test a origini dati esterne, ad esempio file CSV, file XML o database.
Per informazioni dettagliate, vedere:
ITestDataSource
L'interfaccia ITestDataSource consente di creare attributi di origine dati completamente personalizzati. Implementare questa interfaccia quando è necessario un comportamento che gli attributi predefiniti non supportano, ad esempio la generazione di dati di test in base a variabili di ambiente, file di configurazione o altre condizioni di runtime.
Membri di interfaccia
L'interfaccia definisce due metodi:
| Metodo | Scopo |
|---|---|
GetData(MethodInfo) |
Restituisce i dati di test come IEnumerable<object?[]> |
GetDisplayName(MethodInfo, object?[]?) |
Restituisce il nome visualizzato per un test case |
Creazione di un attributo di origine dati personalizzato
Per creare un'origine dati personalizzata, definire una classe di attributi che eredita da Attribute e implementa 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);
}
}
Esempio reale: dati di test basati sull'ambiente
Questo esempio mostra un attributo personalizzato che genera dati di test basati su framework di destinazione, filtrando in base al 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);
}
}
Suggerimento
Per semplici casi in cui è sufficiente personalizzare i nomi visualizzati o aggiungere metadati, è consigliabile usare TestDataRow<T> con DynamicData anziché implementare ITestDataSource. Riservare implementazioni personalizzate ITestDataSource per scenari che richiedono filtri dinamici o logica di generazione di dati complessa.
Strategia di dispiegamento
Gli attributi di test basati sui dati supportano la TestDataSourceUnfoldingStrategy proprietà , che controlla la modalità di visualizzazione dei test case nei risultati di Esplora test e TRX. Questa proprietà determina anche se è possibile eseguire singoli test case in modo indipendente.
Strategie disponibili
| Strategia | Comportamento |
|---|---|
Auto (impostazione predefinita) |
MSTest determina la strategia migliore |
Unfold |
Tutti i test case vengono espansi e visualizzati singolarmente |
Fold |
Tutti i test case vengono compressi in un singolo nodo di test |
Quando modificare la strategia
Per la maggior parte degli scenari, il comportamento predefinito Auto offre il miglior equilibrio. Prendere in considerazione la modifica di questa impostazione solo quando si hanno requisiti specifici:
- Origini dati non deterministiche
- Limitazioni o bug noti in MSTest
- Problemi di prestazione con molti test case
Esempio di utilizzo
[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");
}
}
Procedure consigliate
Scegliere l'attributo corretto: usare
DataRowper dati semplici e inline. UsareDynamicDataper dati complessi o calcolati.Assegnare un nome ai test case: usare
DisplayNameper semplificare l'identificazione degli errori di test.Mantenere vicine le origini dati: definire le origini dati nella stessa classe quando possibile per una migliore gestibilità.
Usare dati significativi: scegliere i dati di test che esercitano casi limite e condizioni limite.
Considerare i test combinatori: per testare le combinazioni di parametri, usare il pacchetto Combinatorial.MSTest .