11. Tests unitaires
Sections
Réglages de lecture
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Jusqu’ici, check.sh vérifiait les programmes de ce cours en comparant toute leur sortie avec un fichier. Un test unitaire vérifie directement une méthode : il l’appelle avec des arguments connus et compare le résultat avec la valeur qu’elle devrait renvoyer. Un test est lui aussi un petit programme. Un framework de test trouve les tests, exécute chacun d’eux, compte les échecs et dit quel test a échoué et pourquoi. Cette leçon utilise xUnit, le framework du modèle dotnet new xunit, pour tester des méthodes des leçons 4, 8 et 10.
Le code de cette leçon se trouve dans code/csharp-beginner : examples/l11_by_hand.cs, et le dossier l11-tests avec trois projets. Fretboard est une bibliothèque, Fretboard.Tests contient ses tests et les solutions des exercices, et les tests de Pitfalls.Tests échouent exprès. check.sh exécute dotnet test sur les deux projets de test et compare un résumé avec les fichiers de expected/.
Un test à la main
Section intitulée « Un test à la main »Un test n’a pas besoin de framework. Ce programme appelle quatre fois la méthode FretFrequency de la leçon 4 et compare chaque résultat avec la valeur qu’il attend :
// Un test à la main : appeler une méthode, comparer son résultat avec la valeur attendue, et dire quelle vérification a échouéint failed = 0;Check("fret 12 of A2", FretFrequency(110.0, 12), 220.0);Check("fret 7 of A2", FretFrequency(110.0, 7), 164.81);Check("fret 5 of E2", FretFrequency(82.41, 5), 110.0);Check("fret 1 of B3", FretFrequency(246.94, 1), 261.63); // C4 dans une table des fréquences des notesConsole.WriteLine($"{failed} failed");
void Check(string name, double actual, double expected){ if (actual == expected) { Console.WriteLine($"ok {name}"); } else { Console.WriteLine($"FAIL {name}: expected {expected}, got {actual}"); failed++; }}
// La méthode de la leçon 4double FretFrequency(double openString, int fret){ double frequency = openString * Math.Pow(2, fret / 12.0); return Math.Round(frequency, 2);}ok fret 12 of A2ok fret 7 of A2ok fret 5 of E2FAIL fret 1 of B3: expected 261.63, got 261.621 failedLa dernière vérification échoue, et pourtant la méthode n’est pas fausse. La valeur attendue, 261,63 Hz, est le do central (do4) dans une table de fréquences. La méthode part de 246,94, la corde de si à vide déjà arrondie à deux décimales, et une case plus haut donne 261,62. Un test qui échoue dit seulement que deux valeurs diffèrent : le code peut être faux, ou bien la valeur attendue, et c’est à toi de trouver lequel.
Le programme se termine aussi avec le code de sortie 0, comme si tout avait réussi : un script qui le lance ne peut pas savoir qu’une vérification a échoué. check.sh fonctionne de la même façon, avec ses lignes ok et FAIL, mais il fixe lui-même son code de sortie. Un framework de test fait ce travail pour toi.
Une bibliothèque et un projet de test
Section intitulée « Une bibliothèque et un projet de test »Un projet de test ne peut pas appeler les méthodes d’une application basée sur un fichier. Les méthodes vont dans une bibliothèque de classes, un projet sans instructions de niveau supérieur que d’autres projets utilisent, et les tests vont dans un second projet qui la référence. Trois commandes créent les deux à partir d’un dossier vide, avec les modèles du SDK :
dotnet new classlib -n Fretboarddotnet new xunit -n Fretboard.Testsdotnet add Fretboard.Tests reference FretboardLes deux premières affichent The template "Class Library" was created successfully. et The template "xUnit Test Project" was created successfully., puis restaurent les packages ; la troisième affiche Reference `..\Fretboard\Fretboard.csproj` added to the project. sous Windows. Chaque modèle ajoute aussi un fichier d’exemple, Class1.cs et UnitTest1.cs, que cette leçon a supprimés.
La bibliothèque contient les méthodes des leçons précédentes, inchangées, sous forme de méthodes public static d’une classe de l’espace de noms Fretboard :
namespace Fretboard;
// Des méthodes des leçons précédentes, déplacées dans une bibliothèque pour qu'un projet de test puisse les appelerpublic static class Guitar{ static readonly string[] Chromatic = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
// Leçon 4 : la fréquence d'une case, arrondie à deux décimales public static double FretFrequency(double openString, int fret) { double frequency = openString * Math.Pow(2, fret / 12.0); return Math.Round(frequency, 2); }
// Leçon 4, exercice 2 : une nouvelle liste où chaque note est déplacée d'un nombre de demi-tons public static List<string> Transpose(List<string> notes, int semitones) { List<string> result = []; foreach (string note in notes) { int index = Array.IndexOf(Chromatic, note); int moved = ((index + semitones) % 12 + 12) % 12; // + 12 garde les déplacements négatifs dans 0..11 result.Add(Chromatic[moved]); } return result; }
// Leçon 8, exercice 2 : un numéro de case lu dans du texte, de 0 à 24 public static int ParseFret(string text) { int fret = int.Parse(text); ArgumentOutOfRangeException.ThrowIfNegative(fret); ArgumentOutOfRangeException.ThrowIfGreaterThan(fret, 24); return fret; }}Un second fichier, Project.cs, contient le record Project de la leçon 10, avec une méthode Parse pour une ligne de data/ga-projects.csv. Le fichier du projet de test, Fretboard.Tests.csproj, liste ce que le modèle a choisi avec le SDK 10.0.112 :
<ItemGroup> <PackageReference Include="coverlet.collector" Version="6.0.4" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> <PackageReference Include="xunit" Version="2.9.3" /> <PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" /></ItemGroup>
<ItemGroup> <Using Include="Xunit" /></ItemGroup>
<ItemGroup> <ProjectReference Include="..\Fretboard\Fretboard.csproj" /></ItemGroup>xunitapporte les attributs etAssert, et<Using Include="Xunit" />les rend disponibles dans tous les fichiers du projet sansusing.xunit.runner.visualstudioetMicrosoft.NET.Test.Sdkpermettent àdotnet testet aux éditeurs de trouver et d’exécuter les tests ;coverlet.collectormesure quelles lignes les tests ont exécutées, quand tu le demandes.ProjectReferenceest la ligne qu’a écritedotnet add reference: les tests peuvent maintenant utiliser les classes publiques de la bibliothèque.
Le modèle crée un projet xUnit v2. Le site de xUnit dit que la v2 est en mode maintenance, et encourage à utiliser la v3 pour tout nouveau travail ; cette leçon garde ce que donne le modèle.
Un premier test
Section intitulée « Un premier test »Un test est une méthode public avec l’attribut [Fact], dans une classe public :
// Un fait : un seul cas, sans paramètre [Fact] public void FretFrequency_TwelfthFret_DoublesTheFrequency() { // Arrange : la corde de la à vide double openA = 110.0;
// Act double result = Guitar.FretFrequency(openA, 12);
// Assert : la valeur attendue vient en premier, la valeur réelle en second Assert.Equal(220.0, result); }La plupart des tests ont trois étapes : arrange, préparer les données ; act, agir en appelant la méthode ; assert, vérifier que le résultat est celui attendu. Assert.Equal lève une exception quand ses deux valeurs diffèrent, et xUnit compte alors le test comme échoué. Le nom du test dit ce qu’il teste, dans quelle situation, et ce qui doit se passer : quand il échoue, le nom suffit à dire ce qui est cassé.
dotnet test compile la bibliothèque et le projet de test, puis exécute tous les tests :
dotnet test l11-tests/Fretboard.TestsLa commande affiche les lignes de la restauration et de la compilation, puis une ligne par projet de test. check.sh garde cette dernière ligne, sans sa durée, qui change à chaque exécution :
Passed! - Failed: 0, Passed: 26, Skipped: 0, Total: 26 - Fretboard.Tests.dll (net10.0)Les 26 tests sont ceux de cette leçon et les solutions de ses exercices. La commande se termine avec le code de sortie 0, et avec 1 dès qu’un test échoue : un script ou une tâche de CI voit l’échec sans lire le texte.
Les records se comparent par valeur (leçon 6), donc un seul Assert.Equal vérifie les quatre champs que lit Project.Parse, et Assert.Throws vérifie qu’une méthode lève une exception :
[Fact] public void Parse_ReadsTheFourFields() { Project project = Project.Parse("GA.Core,C#,67,0");
// Les records se comparent par valeur : un seul Assert vérifie les quatre champs Assert.Equal(new Project("GA.Core", "C#", 67, 0), project); }
[Fact] public void Parse_RefusesALineWithoutNumbers() { Assert.Throws<FormatException>(() => Project.Parse("GA.Core,C#,many,0")); }La lambda, () => Project.Parse(...), donne à Assert.Throws l’appel à faire au lieu de le faire : l’exception se produit à l’intérieur d’Assert.Throws, qui l’attrape et vérifie son type.
Plusieurs cas dans un seul test
Section intitulée « Plusieurs cas dans un seul test »Une théorie, [Theory], est un test avec des paramètres, et chaque attribut [InlineData] lui donne une ligne d’arguments :
// Une théorie : le même test avec plusieurs lignes de données, chaque ligne étant un test à part [Theory] [InlineData(110.0, 0, 110.0)] [InlineData(110.0, 7, 164.81)] [InlineData(82.41, 5, 110.0)] [InlineData(110.0, 12, 220.0)] public void FretFrequency_RoundsToTwoDecimals(double openString, int fret, double expected) { Assert.Equal(expected, Guitar.FretFrequency(openString, fret)); }xUnit compte chaque ligne comme un test à part : avec cette théorie, la première version du projet avait 15 tests, pas 12. Une ligne qui échoue est signalée avec ses arguments, comme le montre la section suivante, et les autres lignes s’exécutent quand même.
Quand un test échoue
Section intitulée « Quand un test échoue »Les quatre tests de Pitfalls.Tests échouent exprès. Chacun fait une erreur que font les débutants, et dotnet test l’explique :
// 0.1 + 0.2 ne vaut pas exactement 0.3 dans un double [Fact] public void Doubles_ComparedBitForBit() { Assert.Equal(0.3, 0.1 + 0.2); }
// ParseFret lève ArgumentOutOfRangeException, un type dérivé d'ArgumentException [Fact] public void Throws_WithTheBaseType() { Assert.Throws<ArgumentException>(() => Guitar.ParseFret("25")); }
// Les arguments sont inversés : le message appelle « Expected » le résultat de la méthode [Fact] public void Equal_WithTheArgumentsSwapped() { Assert.Equal(Guitar.FretFrequency(110.0, 7), 164.8); }
// Une ligne de la théorie est fausse : E monté d'un demi-ton donne F [Theory] [InlineData("C", 2, "D")] [InlineData("B", 1, "C")] [InlineData("E", 1, "F#")] public void Transpose_OneNote(string note, int semitones, string expected) { Assert.Equal([expected], Guitar.Transpose([note], semitones)); }check.sh garde les avertissements de la compilation, le message de chaque test échoué et la dernière ligne. Il retire les chemins, les durées et la pile d’appels (stack trace), la liste des appels de méthodes qui ont mené à l’échec, et il trie les tests échoués par nom : xUnit ne les signale pas dans l’ordre du fichier.
PitfallTests.cs(26,9): warning xUnit2000: The literal or constant value 164.8 should be passed as the 'expected' argument in the call to 'Assert.Equal(expected, actual)' in method 'Equal_WithTheArgumentsSwapped' on type 'PitfallTests'. Swap the parameter values. (https://xunit.net/xunit.analyzers/rules/xUnit2000) Failed Pitfalls.Tests.PitfallTests.Doubles_ComparedBitForBit Error Message: Assert.Equal() Failure: Values differExpected: 0.29999999999999999Actual: 0.30000000000000004 Failed Pitfalls.Tests.PitfallTests.Equal_WithTheArgumentsSwapped Error Message: Assert.Equal() Failure: Values differExpected: 164.81Actual: 164.80000000000001 Failed Pitfalls.Tests.PitfallTests.Throws_WithTheBaseType Error Message: Assert.Throws() Failure: Exception type was not an exact matchExpected: typeof(System.ArgumentException)Actual: typeof(System.ArgumentOutOfRangeException)---- System.ArgumentOutOfRangeException : fret ('25') must be less than or equal to '24'. (Parameter 'fret')Actual value was 25. Failed Pitfalls.Tests.PitfallTests.Transpose_OneNote(note: "E", semitones: 1, expected: "F#") Error Message: Assert.Equal() Failure: Collections differ ↓ (pos 0)Expected: ["F#"]Actual: ["F"] ↑ (pos 0)Failed! - Failed: 4, Passed: 2, Skipped: 0, Total: 6 - Pitfalls.Tests.dll (net10.0)Les 2 tests qui passent sont les deux premières lignes de la théorie. Lis chaque message depuis le haut :
- Les nombres double. Un
doublene peut représenter exactement ni 0,1 ni 0,2 ni 0,3, donc0.1 + 0.2et0.3sont deux nombres légèrement différents. xUnit affiche les deux avec 17 chiffres pour montrer la différence : même0.3devient0.29999999999999999. Compare les double à un nombre de décimales près,Assert.Equal(0.3, 0.1 + 0.2, 10), qui passe. Les tests deFretFrequencyn’en ont pas besoin : la méthode arrondit avecMath.Round, et le résultat arrondi est le mêmedoubleque le littéral164.81du test. - Les types d’exception.
Assert.Throws<ArgumentException>veut exactement ce type.ParseFretlèveArgumentOutOfRangeException, une classe dérivée d’ArgumentException(leçon 8) : le message montre les deux types, et l’exception qui a été levée. Donne le type exact, ou utiliseAssert.ThrowsAny<ArgumentException>, qui accepte aussi les types dérivés. - Les arguments inversés. La valeur attendue vient en premier. Ici, le littéral est en second, donc le message appelle « Expected » le résultat de la méthode, 164,81, et « Actual » la valeur fausse, 164,8 : le contraire de la vérité. Le compilateur a averti avant que les tests ne s’exécutent : xUnit2000 vient d’un analyseur, une vérification que le package
xunitajoute au compilateur. - Une ligne fausse. Le nom du test montre les arguments de la ligne qui a échoué, et les flèches pointent vers le premier élément qui diffère. Cette fois, le code est juste et le test est faux : mi (E) monté d’un demi-ton donne fa (F).
Les corrections des deux premiers sont dans Fretboard.Tests ; les deux derniers sont l’exercice 3 :
// Assert.ThrowsAny accepte aussi un type qui en dérive : ArgumentOutOfRangeException est une ArgumentException [Fact] public void ParseFret_RefusesANegativeFret() { Assert.ThrowsAny<ArgumentException>(() => Guitar.ParseFret("-1")); }
// Ici, deux double sont comparés à 10 décimales près, pas bit à bit [Fact] public void Doubles_CompareWithAPrecision() { Assert.Equal(0.3, 0.1 + 0.2, 10); }Un objet par test
Section intitulée « Un objet par test »xUnit crée un nouvel objet de la classe de test pour chaque test. Un champ ne transporte rien d’un test au suivant :
// xUnit crée un nouvel objet de la classe de test pour chaque test, donc un champ repart de 0 dans chaque testpublic class InstanceTests{ int _count;
[Fact] public void FirstTest() { _count++; Assert.Equal(1, _count); }
[Fact] public void SecondTest() { _count++; Assert.Equal(1, _count); }}Les deux tests passent : chacun voit _count à 0. Un test ne peut pas compter sur le fait qu’un autre s’est exécuté avant lui, et il n’en a pas besoin : chaque test prépare ses propres données.
Tester ce que la bibliothèque rend public
Section intitulée « Tester ce que la bibliothèque rend public »Le projet de test est un autre programme : il appelle la bibliothèque de l’extérieur, et ne voit que ses membres public. Un membre sans public est privé à sa classe, comme dans cet extrait, où les instructions de niveau supérieur jouent le rôle du test :
// Un test appelle la bibliothèque de l'extérieur : il ne voit que ce que la bibliothèque rend publicConsole.WriteLine(Guitar.Chromatic.Length);
static class Guitar{ static readonly string[] Chromatic = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];}l11_private.cs(2,26): error CS0122: 'Guitar.Chromatic' is inaccessible due to its protection levelCS0122 dit que le membre existe mais qu’on ne peut pas l’atteindre d’ici. Depuis un projet de test, un membre marqué internal, visible seulement dans son propre projet, donne une autre erreur : dans une paire de projets d’essai, le compilateur a répondu CS0117, 'Lab' does not contain a definition for 'Secret', comme si la méthode n’existait pas. L’attribut InternalsVisibleTo peut ouvrir les membres internes d’une bibliothèque à ses tests. Tester à travers les méthodes publiques vaut en général mieux : les tests vérifient alors ce qu’utilisent les autres projets, et ils passent encore quand tu réécris l’intérieur d’une méthode.
Dans Guitar Alchemist
Section intitulée « Dans Guitar Alchemist »Au commit 5c3a52a, Guitar Alchemist a 18 projets de test. 16 fichiers de projet référencent NUnit, un autre framework de test, et 3 référencent xUnit. Les idées sont les mêmes, avec d’autres noms : [Test] pour [Fact], [TestCase] pour [InlineData], Assert.That(actual, Is.EqualTo(expected)) pour Assert.Equal(expected, actual), et [Ignore("reason")] pour ignorer un test.
Les tests des fichiers YAML. La leçon 9 a trouvé que trois des quatre fichiers YAML des services de connaissances musicales de GA ne se chargent pas, ce qui est maintenant l’issue GA n° 797. GA a des tests pour ces services, dans MusicalKnowledgeServiceTests.cs. Trois de ses huit tests sont ignorés avec [Ignore("Configuration files not loaded in test environment")], et parmi les cinq autres, trois vérifient seulement qu’un nombre est supérieur à zéro ou qu’une liste n’est pas vide. Une sonde a compilé ce fichier sans le modifier, avec les services de GA et les mêmes packages NUnit, et l’a exécuté : 5 tests ont réussi, 3 ont été ignorés, aucun n’a échoué. La recherche de « jazz » a réussi avec un résultat : la progression d’accords par défaut que le chargeur garde après son échec. Un test qui vérifie « plus que zéro » ne peut pas voir un chargeur qui échoue et renvoie un seul élément par défaut ; un test qui vérifie le vrai nombre d’éléments, ou que le chargement ne signale aucune erreur, le pourrait.
Une préparation qui ne s’exécute jamais. Le même projet de test a une classe, TestEnvironment, marquée [SetUpFixture], qui déplace le dossier courant à la racine du dépôt. Son commentaire dit qu’elle « ensures configuration-backed services can locate their YAML/JSON inputs during tests » (permet aux services qui dépendent de la configuration de trouver leurs fichiers YAML/JSON pendant les tests) : le problème de la leçon 10, résolu pour les tests. Mais NUnit n’exécute une setup fixture qu’avant les tests de son propre espace de noms, et celle-ci est seule dans GA.Business.Core.Tests.TestBootstrap. Un test de la sonde, placé à côté des tests YAML, a trouvé le dossier courant toujours dans son dossier bin, et GA_TEST_MODE, que la préparation définit, non défini. Les fichiers YAML sont trouvés quand même, à côté du programme, là où la compilation les copie.
Des tests qui ne sont pas compilés. Le fichier de projet retire 35 fichiers de la compilation, sous le commentaire « Exclude test files with missing service implementations » (exclure les fichiers de test dont les implémentations de services manquent). Cinq des noms ne correspondent plus à aucun fichier ; les 30 autres fichiers contiennent 333 lignes qui commencent par [Test] ou [TestCase]. Un test qui n’est pas compilé n’échoue jamais, et dotnet test n’en parle pas.
Le tableau QA du journal consigne ces mesures.
Exercices
Section intitulée « Exercices »Les solutions sont des tests de Fretboard.Tests, dans ExerciseTests.cs : dotnet test les exécute avec les autres.
Exercice 1 — transpose vers le bas
Section intitulée « Exercice 1 — transpose vers le bas »Écris un test qui vérifie que Guitar.Transpose(["A", "C", "E"], -3) renvoie F#, A et C#.
Solution
// Exercice 1 : transposer vers le bas [Fact] public void Transpose_MovesEachNoteDown() { List<string> result = Guitar.Transpose(["A", "C", "E"], -3);
Assert.Equal(["F#", "A", "C#"], result); }Assert.Equal compare deux listes élément par élément ; l’expression de collection ["F#", "A", "C#"] prend le type de l’autre argument.
Exercice 2 — les limites de ParseFret
Section intitulée « Exercice 2 — les limites de ParseFret »Les bogues se cachent aux limites. Teste que ParseFret accepte "0" et "24", refuse "-1" et "25" avec ArgumentOutOfRangeException, et refuse "seven" avec FormatException. Utilise des théories là où un test a plusieurs lignes.
Solution
// Exercice 2 : les limites de ParseFret [Theory] [InlineData("0", 0)] [InlineData("24", 24)] public void ParseFret_AcceptsTheFirstAndLastFret(string text, int expected) { Assert.Equal(expected, Guitar.ParseFret(text)); }
[Theory] [InlineData("-1")] [InlineData("25")] public void ParseFret_RefusesTheFretsJustOutside(string text) { Assert.Throws<ArgumentOutOfRangeException>(() => Guitar.ParseFret(text)); }
[Fact] public void ParseFret_RefusesAWord() { Assert.Throws<FormatException>(() => Guitar.ParseFret("seven")); }Chaque limite a une valeur juste à l’intérieur, 0 et 24, et une valeur juste à l’extérieur, -1 et 25. Si quelqu’un écrit ThrowIfGreaterThan(fret, 23) par erreur, la ligne "24" échoue.
Exercice 3 — corrige les deux tests faux
Section intitulée « Exercice 3 — corrige les deux tests faux »Deux tests de Pitfalls.Tests attendent la mauvaise valeur : Equal_WithTheArgumentsSwapped et la ligne "E", 1, "F#" de Transpose_OneNote. Écris une version correcte de chacun.
Solution
// Exercice 3 : les deux pièges dont la valeur attendue était fausse, corrigés [Fact] public void Equal_WithTheExpectedValueFirst() { Assert.Equal(164.81, Guitar.FretFrequency(110.0, 7)); }
[Theory] [InlineData("C", 2, "D")] [InlineData("B", 1, "C")] [InlineData("E", 1, "F")] public void Transpose_OneNote(string note, int semitones, string expected) { Assert.Equal([expected], Guitar.Transpose([note], semitones)); }La première correction met le littéral en premier et utilise la bonne valeur, 164,81 : l’avertissement xUnit2000 disparaît aussi. Dans la seconde, le code était juste : la correction est dans le test.
Exercice 4 — un test qui lit un fichier
Section intitulée « Exercice 4 — un test qui lit un fichier »Écris un test qui lit data/ga-projects.csv avec Project.Parse et vérifie qu’il contient 12 projets et 632 fichiers source, .cs et .fs ensemble. Deux points de la leçon 10 comptent ici : le fichier doit être copié à côté des tests compilés, et un test ne devrait pas dépendre du dossier courant.
Solution
Un élément None dans Fretboard.Tests.csproj copie le fichier dans le dossier de sortie, sous data :
<ItemGroup> <None Include="..\..\data\ga-projects.csv" Link="data\ga-projects.csv" CopyToOutputDirectory="PreserveNewest" /></ItemGroup>Le test construit le chemin à partir d’AppContext.BaseDirectory, le dossier des tests compilés :
// Exercice 4 : un test qui lit data/ga-projects.csv, copié à côté des tests par Fretboard.Tests.csproj [Fact] public void GaProjects_HoldSixHundredThirtyTwoSourceFiles() { string path = Path.Combine(AppContext.BaseDirectory, "data", "ga-projects.csv");
List<Project> projects = File.ReadLines(path).Skip(1).Select(Project.Parse).ToList();
Assert.Equal(12, projects.Count); Assert.Equal(632, projects.Sum(p => p.SourceFiles)); }Select(Project.Parse) donne à LINQ la méthode elle-même, comme Count(IsFSharp) à la leçon 9. 632, ce sont les 557 fichiers C# et les 75 fichiers F# de la leçon 10.
Ce qu’il faut retenir
Section intitulée « Ce qu’il faut retenir »- Un test unitaire appelle une méthode avec des arguments connus et compare son résultat avec la valeur attendue. Un framework de test exécute tous les tests, compte les échecs et fixe le code de sortie.
- Les tests vont dans un projet de test qui référence une bibliothèque de classes :
dotnet new classlib,dotnet new xunit,dotnet add reference.dotnet testcompile les deux et exécute les tests. [Fact]marque un test ;[Theory]avec[InlineData]exécute le même test une fois par ligne, et chaque ligne compte comme un test.Assert.Equal(expected, actual): la valeur attendue en premier. Compare les double à un nombre de décimales près.Assert.Throws<T>veut le type exact ;Assert.ThrowsAny<T>accepte les types dérivés.- Un test qui échoue dit que deux valeurs diffèrent : vérifie si c’est le code ou le test qui est faux avant de changer l’un ou l’autre.
- xUnit crée un nouvel objet pour chaque test, et ne promet aucun ordre. Un projet de test ne voit que les membres publics de la bibliothèque.
- Un test ignoré, non compilé, ou qui vérifie seulement « plus que zéro » peut passer alors que le code est cassé.
La suite : un petit projet, une solution avec cette bibliothèque, une application console et ces tests, un package NuGet, et un premier regard sur async.
- xUnit : premiers pas avec xUnit.net v2, règle d’analyseur xUnit2000
- .NET : tests unitaires C# avec xUnit, bonnes pratiques des tests unitaires,
dotnet test, modèlesdotnet new,dotnet add reference,InternalsVisibleTo,ArgumentOutOfRangeException,AppContext.BaseDirectory, éléments MSBuild - NUnit : documentation,
SetUpFixture - Erreurs du compilateur CS0122 et CS0117