Écriture de tests unitaires en C# (xUnit, NUnit, MSTest)
Apprenez à écrire des tests unitaires en C# avec xUnit, NUnit et MSTest pour améliorer la qualité du code.
En développement logiciel, les tests unitaires (unit tests) permettent de vérifier que les plus petites parties d’une application (méthodes, classes ou fonctions) fonctionnent correctement de manière isolée. En C#, les frameworks de test les plus utilisés sont xUnit, NUnit et MSTest. Les tests unitaires aident à détecter les erreurs à un stade précoce, améliorent la qualité du code et permettent un refactoring en toute sécurité.
Qu’est-ce qu’un test unitaire ?
Les tests unitaires vérifient si une méthode produit le résultat attendu pour des entrées données. Les tests sont généralement isolés pour chaque scénario et indépendants des dépendances externes (base de données, système de fichiers, accès réseau).
// Exemple : méthode à tester
public class Calculatrice
{
public int Additionner(int a, int b) => a + b;
}
Dans l’exemple ci-dessus, la méthode Additionner représente l’unité à tester.
Créer un projet de test
Vous pouvez facilement créer un projet de test avec Visual Studio ou la CLI .NET :
// Créer un projet de test avec xUnit
dotnet new xunit -n Calculatrice.Tests
cd Calculatrice.Tests
// Ajouter une référence au projet principal
dotnet add reference ../Calculatrice/Calculatrice.csproj
Vous pouvez également utiliser les modèles nunit ou mstest :
dotnet new nunit -n NomDuProjet.Tests
dotnet new mstest -n NomDuProjet.Tests
Écriture de tests avec xUnit
xUnit est un framework moderne et flexible, largement adopté dans l’écosystème C#.
L’attribut [Fact] désigne des tests indépendants, tandis que [Theory] est utilisé pour les tests paramétrés.
using Xunit;
public class CalculatriceTests
{
[Fact]
public void Additionner_DoîtRetournerLeRésultatCorrect()
{
// Arrange
var c = new Calculatrice();
// Act
int resultat = c.Additionner(2, 3);
// Assert
Assert.Equal(5, resultat);
}
[Theory]
[InlineData(1, 2, 3)]
[InlineData(-1, 5, 4)]
[InlineData(10, -2, 8)]
public void Additionner_AvecDifférentesValeurs_DoîtFonctionner(int a, int b, int attendu)
{
var c = new Calculatrice();
Assert.Equal(attendu, c.Additionner(a, b));
}
}
Fact : teste un seul scénario. Theory : teste la même méthode avec plusieurs jeux de données.
Écriture de tests avec NUnit
NUnit est un framework de test puissant et anciennement établi dans le monde C#. Il est reconnu pour sa simplicité et sa grande variété d’assertions.
using NUnit.Framework;
[TestFixture]
public class CalculatriceTests
{
private Calculatrice calc;
[SetUp]
public void Setup()
{
calc = new Calculatrice();
}
[Test]
public void Additionner_Test()
{
int resultat = calc.Additionner(4, 6);
Assert.That(resultat, Is.EqualTo(10));
}
[TestCase(1, 2, 3)]
[TestCase(5, 5, 10)]
public void Additionner_AvecPlusieursValeurs_Test(int a, int b, int attendu)
{
Assert.That(calc.Additionner(a, b), Is.EqualTo(attendu));
}
}
Dans NUnit, la méthode [SetUp] s’exécute avant chaque test,
et [TestCase] est utilisée pour les tests paramétrés.
Écriture de tests avec MSTest
MSTest est le framework de test intégré à Visual Studio par Microsoft. Il est particulièrement populaire dans les projets d’entreprise.
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class CalculatriceTests
{
private Calculatrice calc;
[TestInitialize]
public void Init()
{
calc = new Calculatrice();
}
[TestMethod]
public void Additionner_Test()
{
int resultat = calc.Additionner(2, 8);
Assert.AreEqual(10, resultat);
}
[DataTestMethod]
[DataRow(1, 1, 2)]
[DataRow(-3, 5, 2)]
public void Additionner_AvecDifférentesValeurs_Test(int a, int b, int attendu)
{
Assert.AreEqual(attendu, calc.Additionner(a, b));
}
}
Dans MSTest, [TestInitialize] est utilisée pour les préparations avant chaque test,
et [DataTestMethod] pour les tests basés sur des données.
Méthodes d’assertion (Assert)
Tous les frameworks de test incluent des structures d’assertion similaires :
Assert.Equal(expected, actual)→ Vérifie l’égalité des valeurs.Assert.NotNull(obj)→ Vérifie que l’objet n’est pas nul.Assert.True(condition)/Assert.False(condition)Assert.Throws<Exception>(() => ...)→ Vérifie qu’une exception attendue est levée.
// Exemple : Test d’exception
[Fact]
public void Division_DoitLeverDivideByZeroException()
{
var calc = new Calculatrice();
Assert.Throws<DivideByZeroException>(() => calc.Diviser(5, 0));
}
Exécution des tests
Les tests peuvent être exécutés via la CLI ou dans Visual Studio :
// Exécuter les tests avec .NET CLI
dotnet test
Dans Visual Studio, la fenêtre “Test Explorer” permet d’exécuter les tests et d’afficher les résultats réussis ou échoués dans une interface graphique.
Mocking et isolation des dépendances
Le mocking est utilisé pour isoler les dépendances réelles de l’environnement de test. Cela garantit que les tests ne valident que la méthode cible, sans être affectés par des facteurs externes (base de données, service, etc.).
using Moq;
using Xunit;
public interface IServiceEmail
{
void Envoyer(string adresse, string message);
}
public class EnregistrementUtilisateur
{
private readonly IServiceEmail _email;
public EnregistrementUtilisateur(IServiceEmail email) => _email = email;
public void Enregistrer(string nom)
{
_email.Envoyer("admin@site.com", $"{nom} a été enregistré.");
}
}
public class EnregistrementUtilisateurTests
{
[Fact]
public void Enregistrer_DoitEnvoyerUnEmail()
{
var mock = new Mock<IServiceEmail>();
var enregistrement = new EnregistrementUtilisateur(mock.Object);
enregistrement.Enregistrer("Jean");
mock.Verify(m => m.Envoyer("admin@site.com", "Jean a été enregistré."), Times.Once);
}
}
La bibliothèque Moq permet de créer des objets simulés (mocks) afin d’isoler l’environnement de test.
Organisation et nommage des tests
- Les noms de tests doivent être explicites :
NomMethode_Scenario_ResultatAttendu. - Chaque test doit vérifier un seul comportement.
- Suivez le modèle Arrange–Act–Assert (Préparer–Agir–Vérifier).
- Minimisez les dépendances entre les tests.
- Les tests doivent être indépendants les uns des autres.
Exemple réel : Test de calcul du total d’une commande
L’exemple suivant teste le calcul du total d’une commande.
// Code applicatif
public class Commande
{
public List<decimal> PrixProduits { get; set; } = new();
public decimal Total() => PrixProduits.Sum();
}
// Test
public class CommandeTests
{
[Fact]
public void Total_DoitRetournerLeBonResultat()
{
var c = new Commande();
c.PrixProduits.AddRange(new[] { 10m, 20m, 30m });
Assert.Equal(60m, c.Total());
}
}
Cet exemple montre un test unitaire simple qui garantit le bon fonctionnement de la logique métier.
Performance et meilleures pratiques
- Chaque test doit être isolé et ne pas dépendre de l’état global.
- Gardez les tests courts — un test doit vérifier un seul comportement.
- Utilisez des mocks ou stubs pour isoler les dépendances externes.
- Intégrez les tests dans votre pipeline d’intégration continue (CI).
- Profilez et optimisez les tests lents.
TL;DR
- Test unitaire : Teste des unités de code isolées et de petite taille.
- xUnit : Framework moderne, minimaliste et largement utilisé dans l’écosystème .NET.
- NUnit : Framework classique avec de puissantes assertions et une approche basée sur les attributs.
- MSTest : Solution Microsoft intégrée, totalement compatible avec Visual Studio.
- Mocking : Isole les dépendances externes grâce à des objets simulés.
- Modèle AAA : Suivez le schéma Arrange–Act–Assert pour structurer vos tests.
- La commande dotnet test exécute tous les tests via la CLI.
Articles connexes
Astuces Visual Studio / VS Code pour C#
Découvrez des astuces Visual Studio et VS Code pour C# afin d’améliorer la productivité et les flux de travail.
Principes de l’Injection de Dépendances en C#
Apprenez les principes de l’Injection de Dépendances en C#, la gestion des dépendances et le couplage faible avec exemples.