Partage via


Créer un test unitaire piloté par les données

Vous pouvez utiliser le framework de test unitaire de Microsoft (MSTest) pour le code managé afin de configurer une méthode de test unitaire permettant de récupérer des valeurs auprès d’une source de données. La méthode est exécutée successivement pour chaque ligne de la source de données, ce qui permet de tester facilement une variété d’entrées en utilisant une même méthode.

Un test unitaire piloté par les données peut utiliser un des types suivants :

  • données inline avec l’attribut DataRow
  • données membres à l’aide de l’attribut DynamicData
  • à partir d’un fournisseur de source connu à l’aide de l’attribut DataSource

Méthode testée

Par exemple, supposons que vous disposez des éléments suivants :

  1. Une solution appelée MyBank, qui accepte et traite les transactions pour différents types de compte.

  2. Un projet dans MyBank, appelé BankDb, qui gère les transactions des comptes.

  3. Une classe appelée Maths dans le projet BankDb, qui remplit les fonctions mathématiques permettant de vérifier que les transactions sont avantageuses pour la banque.

  4. Un projet de test unitaire appelé BankDbTests pour tester le comportement du composant BankDb.

  5. Une classe de tests unitaires appelée MathsTests pour vérifier le comportement de la classe Maths.

Nous allons tester une méthode dans Maths qui additionne deux entiers en utilisant boucle :

public int AddIntegers(int first, int second)
{
    int sum = first;
    for (int i = 0; i < second; i++)
    {
        sum += 1;
    }

    return sum;
}

Tester la méthode de test

Test piloté par les données intégrées

Pour les tests inline, MSTest utilise DataRow pour spécifier les valeurs utilisées par le test piloté par les données. Le test de cet exemple s’exécute successivement pour chaque ligne de données.

[TestMethod]
[DataRow(1, 1, 2)]
[DataRow(2, 2, 4)]
[DataRow(3, 3, 6)]
[DataRow(0, 0, 1)] // The test run with this row fails
public void AddIntegers_FromDataRowTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Test piloté par les données des membres

MSTest utilise DynamicData attribut pour spécifier le nom, le type et le type de définition (par défaut, le type actuel est utilisé) du membre qui fournira les données utilisées par le test piloté par les données.

Remarque

Avant MSTest 3.8, DynamicDataSourceType énumération avait deux membres, Property et Method. La valeur par défaut était Property. À compter de MSTest 3.8, un nouveau membre AutoDetect est ajouté à l’énumération et est la valeur par défaut. Vous n’avez donc plus besoin de spécifier DynamicDataSourceType.

public static IEnumerable<object[]> AdditionData
{
    get
    {
        return new[]
        { 
            new object[] { 1, 1, 2 },
            new object[] { 2, 2, 4 },
            new object[] { 3, 3, 6 },
            new object[] { 0, 0, 1 }, // The test run with this row fails
        };
    }
}

[TestMethod]
[DynamicData(nameof(AdditionData))]
public void AddIntegers_FromDynamicDataTest(int x, int y, int expected)
{
    var target = new Maths();
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Il est également possible de remplacer le nom d’affichage généré par défaut en utilisant la propriété DynamicDataDisplayName de l’attribut DynamicData. La signature de la méthode de nom d'affichage doit être public static string et accepter deux paramètres, le premier de type MethodInfo et le deuxième de type object[].

public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data)
{
    return string.Format("DynamicDataTestMethod {0} with {1} parameters", methodInfo.Name, data.Length);
}

[DynamicData(nameof(AdditionData), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))]

Test piloté par les données du fournisseur source

La création d’un test unitaire piloté par une source de données comprend les étapes suivantes :

  1. Créez une source de données qui contient les valeurs que vous utilisez dans la méthode de test. La source de données peut être de n’importe quel type inscrit sur la machine qui exécute le test.

  2. Ajoutez une propriété TestContext publique de type TestContext à la classe de test.

  3. Créer une méthode de test unitaire

  4. Ajoutez-lui un attribut DataSourceAttribute.

  5. Utilisez la propriété d’indexeur DataRow pour récupérer les valeurs que vous utilisez dans un test.

Créer une source de données

Pour tester la méthode AddIntegers, créez une source de données qui spécifie une plage de valeurs pour les paramètres et la somme qui doit être retournée. Dans cet exemple, nous allons créer une base de données SQL Compact nommée MathsData et une table nommée AddIntegersData, qui contient les noms et les valeurs de colonnes suivants :

FirstNumber SecondNumber Sum
0 1 1
1 1 2
2 -3 -1

Ajouter un TestContext à la classe de test

Le framework de tests unitaires crée un objet TestContext pour stocker les informations de source de données d’un test piloté par les données. Le framework définit ensuite cet objet en tant que valeur de la propriété TestContext que vous créez.

public TestContext TestContext { get; set; }

Dans votre méthode de test, vous accédez aux données via la propriété d’indexeur DataRow de TestContext.

Remarque

.NET Core ne prend pas en charge l’attribut DataSource. Si vous essayez d’accéder aux données de test de cette manière dans un projet de test unitaire .NET Core, UWP ou WinUI, vous verrez une erreur de type « "TestContext" ne contient pas de définition pour "DataRow" et aucune méthode d’extension accessible "DataRow" acceptant un premier argument de type "TestContext" n’a été trouvée (une directive using ou une référence d’assembly est-elle manquante ?) ».

Écrire la méthode de test

La méthode de test pour AddIntegers est relativement simple. Pour chaque ligne de la source de données, appelez AddIntegers avec les valeurs des colonnes PremièreNombre et DeuxièmeNombre en tant que paramètres, puis vérifiez la valeur de retour par rapport à la valeur de la colonne Somme.

[TestMethod]
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")]
public void AddIntegers_FromDataSourceTest()
{
    var target = new Maths();

    // Access the data
    int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
    int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
    int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
    int actual = target.AddIntegers(x, y);
    Assert.AreEqual(expected, actual,
        "x:<{0}> y:<{1}>",
        new object[] {x, y});
}

Spécifier DataSourceAttribute

L’attribut DataSource spécifie la chaîne de connexion de la source de données et le nom de la table que vous utilisez dans la méthode de test. Les informations exactes de la chaîne de connexion varient selon le genre de source de données que vous utilisez. Dans cet exemple, nous avons utilisé une base de données SqlServerCe.

[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0;Data Source=C:\Data\MathsData.sdf", "AddIntegersData")]

Attention

La chaîne de connexion peut contenir des données sensibles (par exemple un mot de passe). La chaîne de connexion est stockée en texte brut dans le code source et dans l’assembly compilé. Limitez l’accès au code source et à l’assembly pour protéger ces informations sensibles.

L’attribut DataSource a trois constructeurs.

[DataSource(dataSourceSettingName)]

Un constructeur avec un seul paramètre utilise les informations de connexion stockées dans le fichier app.config de la solution. dataSourceSettingsName est le nom de l’élément XML contenu dans le fichier de configuration qui spécifie les informations de connexion.

L’utilisation d’un fichier app.config vous permet de changer l’emplacement de la source de données sans apporter de modifications au test unitaire lui-même. Pour plus d’informations sur la création et l’utilisation d’un fichier app.config, consultez Procédure pas à pas : Utilisation d’un fichier de configuration pour définir une source de données.

[DataSource(connectionString, tableName)]

Le constructeur DataSource avec deux paramètres spécifie la chaîne de connexion de la source de données et le nom de la table qui contient les données pour la méthode de test.

Les chaînes de connexion dépendent du type de la source de données, mais elles doivent contenir un élément Provider qui spécifie le nom invariant du fournisseur de données.

[DataSource(
    dataProvider,
    connectionString,
    tableName,
    dataAccessMethod
    )]

Utiliser TestContext.DataRow pour accéder aux données

Pour accéder aux données de la table AddIntegersData, utilisez l’indexeur TestContext.DataRow. Comme DataRow est un objet DataRow, récupérez les valeurs des colonnes via les noms d’index ou de colonnes. Comme les valeurs sont retournées en tant qu’objets, convertissez-les vers le type approprié :

int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);

Exécuter le test et voir les résultats

Quand vous avez fini d’écrire une méthode de test, générez le projet de test. La méthode de test apparaît dans Explorateur de tests dans le groupe Tests non exécutés. Tandis que vous exécutez, écrivez et réexécutez vos tests, l’Explorateur de tests affiche les résultats dans les groupes Échecs de tests, Tests réussis et Tests non exécutés. Vous pouvez choisir Exécuter tout pour exécuter tous vos tests ou Exécuter pour sélectionner un sous-ensemble de tests à exécuter.

La barre de résultats des tests située en haut de Explorateur de tests est animée pendant l'exécution de vos tests. À la fin de la série de tests, la barre est verte si tous les tests ont réussi, ou elle est rouge si un ou plusieurs tests ont échoué. Un résumé de la série de tests s’affiche dans le volet d’informations, en bas de la fenêtre Explorateur de tests. Sélectionnez un test pour en afficher les détails dans le volet inférieur.

Remarque

Il existe un résultat pour chaque ligne de données et un résultat récapitulatif. Si le test a réussi sur chaque ligne de données, le récapitulatif de l’exécution indique Réussite. Si le test a échoué sur une ou plusieurs lignes de données, le récapitulatif de l’exécution indique Échec.

Si vous avez exécuté l’une des méthodes AddIntegers_FromDataRowTest, AddIntegers_FromDynamicDataTest ou AddIntegers_FromDataSourceTest dans notre exemple, la barre de résultats devient rouge et la méthode de test est déplacée vers les tests échoués . Un test piloté par les données échoue si une des méthodes itérées à partir de la source de données échoue. Quand vous choisissez un test piloté par les données qui a échoué dans la fenêtre Explorateur de tests, le volet d’informations affiche les résultats de chaque itération identifiée par l’index de la ligne de données. Dans notre exemple, il apparaît que l’algorithme AddIntegers ne gère pas correctement les valeurs négatives.

Lorsque la méthode sous test est corrigée et que le test est réexécuté, la barre de résultats devient verte et la méthode de test est déplacée vers le groupe Test Réussi.