Tests Unitaires avec XUnit Pour créer un nouveau projet de test avec le framework de tests XUnit : dotnet new xunit -o "MonProjet.Test" Test unitaire Cas de test Pour créer un test unitaire, créez une classe dans ce projet de test. Une classe correspond à une collection de test, chaque méthode est un cas de test. Pour créer un cas de test, ajoutez une méthode avec l'attribut [Fact] : public class MaCollectionDeTests { [Fact] public void MonCasDeTest(){ } } Organisation et nommage Les tests se déroulent en 3 étapes : "Etant donné" ( Given en anglais) : mette en place la situation (souvent les données) requises par le cas de test "Lorsque" ( When en anglais) : appel au code que l'on veut tester dans ce cas de test "Ainsi" ( Then en anglais) : vérifications (assertions) sur les résultats du tests Il convient donc, pour avoir des tests lisible des les diviser entre ces trois étapes, ainsi que de nommer les méthode correspondant au cas de tests de façon à expliciter le contenu de ces étapes. Le pattern suivant est utilisé de manière général pour la nommage : Action_Given_Then . "Action" étant souvent le nom de méthode testée. Exemple : public class CalculatorTests { [Fact] public void Multiply_ValidOperands_ReturnsCorrectResult(){ // Given int leftOperand = 3; int rightOperand = 4; var calculator = new Calculator(); // When int result = calculator.Multiply(leftOperand, rightOperand); // Then Assert.Equal(12, result); } } Et avec un cas négatif, avec une vérification d'exception : [Fact] public void Divide_By0_Throws(){ // Given int leftOperand = 3; int rightOperand = 0; var calculator = new Calculator(); // When Action act = () => calculator.Divide(leftOperand, rightOperand); // Then Assert.Throws(act); } Setup Pour faire un setup commun à tous les cas de tests d'une collection, il suffit d'utilise le constructeur de la classe qui correspond à cette collection, il est appelé pour chaque cas de test. Tests paramétrisés Il est possible de faire des tests paramétrisés, avec l'attribut [Theory] . On peut passer les paramètres de plusieurs manières. Avec l'attribut [InlineData] L'attribut [InlineData] permet de passer des données à un test paramètrisé mais ne supporte que les constantes à la compilation (littéraux de types primitif) : public class CalculatorTests { [Theory] [InlineData(3,4, 12)] [InlineData(2,3, 6)] [InlineData(3,5, 15)] [InlineData(4,4, 16)] public void Multiply_ValidOperands_ReturnsCorrectResult(int leftOperand, int rightOperand, int expectedResult){ // Given var calculator = new Calculator(); // When int result = calculator.Multiply(leftOperand, rightOperand); // Then Assert.Equal(expectedResult, result); } } Cela exécutera autant d'instant de cas de tests que de [InlineData] , ce cas de test en est en fait quatres ! Avec TheoryData TheoryData Permet de passer des données à un test paramètrisé qui ne sont pas des constantes de compilation, et ce de façon fortement typée. public class CalculatorTests { public static TheoryData Data => new TheoryData { { 1, 2, 3 }, { -4, -6, -10 }, { -2, 2, 0 }, { int.MinValue, -1, int.MaxValue } }; [Theory] [MemberData(nameof(Data))] public void Add_ValidOperands_ReturnsCorrectResult(int leftOperand, int rightOperand, int expectedResult){ // Given var calculator = new Calculator(); // When int result = calculator.Add(leftOperand, rightOperand); // Then Assert.Equal(expectedResult, result); } } Assertions XUnit fourni un bon nombre de fonctions d'assertion, comme Assert.Equal ou Assert.Throws , mais certains librairies comme FluentAssertions existent pour faire des assertions un petit peu plus expressives : string actual = "ABCDEFGHI"; actual.Should().StartWith("AB").And.EndWith("HI").And.Contain("EF").And.HaveLength(9); IEnumerable numbers = new[] { 1, 2, 3 }; numbers.Should().OnlyContain(n => n > 0); numbers.Should().HaveCount(4, "because we thought we put four items in the collection");