Tests en C# : Unit, Integration, E2E de A à Z

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : Pourquoi tester en C# ?

\n

Dans le développement moderne en C# (.NET), la qualité du code n'est pas un luxe, mais une nécessité. Les tests permettent non seulement de détecter les bugs avant la mise en production, mais servent également de documentation vivante et accélèrent le refactoring. Il existe trois niveaux principaux de tests qui forment la « pyramide des tests » : Unit (unitaires), Integration (d'intégration) et E2E (de bout en bout). Dans cet article, nous analyserons chacun d'eux avec des exemples de code réel en C#.

\n\n

1. Tests unitaires : Le fondement de la qualité

\n

Les tests unitaires vérifient un fragment de code isolé (méthode, classe) sans dépendances externes (base de données, système de fichiers, API). Dans l'écosystème .NET, trois frameworks sont populaires : xUnit (recommandé pour les nouveaux projets), NUnit et MSTest.

\n\n

Principes fondamentaux (FIRST)

\n
    \n
  • Fast — rapides (millisecondes).
  • \n
  • Isolated — isolés (utilisation de mocks).
  • \n
  • Repeatable — reproductibles.
  • \n
  • Self-validating — auto-validants (Assert).
  • \n
  • Timely — opportuns.
  • \n
\n\n

Exemple de test unitaire avec xUnit et Moq

\n

Supposons que nous ayons un service pour calculer une remise :

\n
public interface IDiscountService\n{\n    decimal CalculateDiscount(decimal price, bool isLoyaltyMember);\n}\n\npublic class DiscountService : IDiscountService\n{\n    public decimal CalculateDiscount(decimal price, bool isLoyaltyMember)\n    {\n        if (isLoyaltyMember) return price * 0.9m; // 10% de remise\n        return price;\n    }\n}
\n

Test utilisant Moq (pour simuler les dépendances) et xUnit :

\n
using Xunit;\nusing Moq;\n\npublic class DiscountServiceTests\n{\n    [Fact]\n    public void CalculateDiscount_WhenLoyaltyMember_ReturnsDiscountedPrice()\n    {\n        // Arrange\n        var service = new DiscountService();\n        var price = 100m;\n\n        // Act\n        var result = service.CalculateDiscount(price, true);\n\n        // Assert\n        Assert.Equal(90m, result);\n    }\n\n    [Theory]\n    [InlineData(100, false, 100)]\n    [InlineData(200, true, 180)]\n    public void CalculateDiscount_MultipleCases_ReturnsCorrectValues(decimal price, bool isLoyalty, decimal expected)\n    {\n        var service = new DiscountService();\n        var result = service.CalculateDiscount(price, isLoyalty);\n        Assert.Equal(expected, result);\n    }\n}
\n

Conseil : Utilisez l'attribut [Fact] pour les tests uniques et [Theory] + [InlineData] pour les tests paramétrés.

\n\n

2. Tests d'intégration : Vérification de l'interconnexion des composants

\n

Les tests d'intégration vérifient comment plusieurs modules fonctionnent ensemble — par exemple, un contrôleur, un service et une base de données réelle (ou son remplacement, comme SQLite in-memory). Dans ASP.NET Core, on utilise WebApplicationFactory pour cela.

\n\n

Configuration de WebApplicationFactory

\n

Créons une classe de test qui démarre un hôte web de test :

\n
using Microsoft.AspNetCore.Mvc.Testing;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.DependencyInjection;\nusing MyApp.Data;\n\npublic class CustomWebApplicationFactory : WebApplicationFactory<Program>\n{\n    protected override void ConfigureWebHost(IWebHostBuilder builder)\n    {\n        builder.ConfigureServices(services =>\n        {\n            // Supprimer le DbContext réel\n            var descriptor = services.SingleOrDefault(\n                d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));\n            if (descriptor != null) services.Remove(descriptor);\n\n            // Ajouter une base de données in-memory\n            services.AddDbContext<AppDbContext>(options =>\n                

Blogs

Book Recommendations