MVAESTECH
Guide .NET · 16 · Production

Publié le · par Mathieu Vaes

Qualité, tests & CI/CD

Une architecture se dégrade dès qu'on cesse de la vérifier. Les tests, les règles d'architecture automatisées et un pipeline fiable maintiennent sa forme dans le temps.

Chapitres du guide
En clair

Les tests automatiques sont un filet de sécurité : à chaque modification, ils vérifient que rien de ce qui marchait n'est cassé. La CI/CD (intégration et déploiement continus) lance ces vérifications automatiquement à chaque commit, puis déploie sans étape manuelle risquée.

La stratégie de test Junior

NiveauCibleOutilsVitesse
UnitaireDomaine, handlers, règlesxUnit v3, NUnit ou MSTest ; NSubstitute ; Shouldly ou AwesomeAssertionsMillisecondes
IntégrationAPI + vraie base en conteneurWebApplicationFactory, Testcontainers, RespawnSecondes
ArchitectureRègle de dépendance, conventionsNetArchTest, ArchUnitNETMillisecondes
ContratCompatibilité producteur / consommateurPact, snapshots OpenAPI avec VerifySecondes
ChargeLatence et débitk6, NBomberMinutes

FluentAssertions est passé sous licence commerciale à partir de la v8 ; AwesomeAssertions (fork communautaire) et Shouldly sont des alternatives libres.

// Test d'intégration avec une vraie base SQL Server en conteneur
public sealed class OrdersApiTests : IAsyncLifetime
{
    private readonly MsSqlContainer _db = new MsSqlBuilder().Build();
    private WebApplicationFactory<Program> _app = null!;

    public async ValueTask InitializeAsync()
    {
        await _db.StartAsync();
        _app = new WebApplicationFactory<Program>().WithWebHostBuilder(b =>
            b.UseSetting("ConnectionStrings:Shop", _db.GetConnectionString()));
    }

    [Fact]
    public async Task Post_order_returns_201_with_location()
    {
        var client = _app.CreateClient();
        var res = await client.PostAsJsonAsync("/v1/orders", new { customerId = Guid.NewGuid() },
                                               TestContext.Current.CancellationToken);
        res.StatusCode.ShouldBe(HttpStatusCode.Created);
        res.Headers.Location.ShouldNotBeNull();
    }

    public async ValueTask DisposeAsync() { await _app.DisposeAsync(); await _db.DisposeAsync(); }
}

// Test d'architecture : le domaine ne dépend de rien
[Fact]
public void Domain_has_no_dependency_on_infrastructure() =>
    Types.InAssembly(typeof(Order).Assembly)
         .ShouldNot().HaveDependencyOnAny("Microsoft.EntityFrameworkCore", "Shop.Infrastructure")
         .GetResult().IsSuccessful.ShouldBeTrue();

Organisation de la solution Confirmé

  • Central Package Management : un Directory.Packages.props fixe chaque version NuGet une seule fois pour toute la solution.
  • Format .slnx : le format de solution XML, lisible et sans conflits de fusion, est le défaut des nouveaux projets depuis le SDK .NET 10.
  • .NET Aspire (Aspire 13) : orchestre en local l'API, la base, Redis et les dépendances avec un tableau de bord OpenTelemetry intégré.
  • Versionnement sémantique des API et des packages internes ; changelog généré depuis des commits conventionnels.

Pipeline CI/CD Confirmé

ÉtapeCommande / outilBloquant
Restore, build (avertissements = erreurs)dotnet build -c ReleaseOui
Formatdotnet format --verify-no-changesOui
Tests + couverturedotnet testOui
Vulnérabilitésdotnet list package --vulnerable --include-transitive, scan d'image (Trivy, Artifact Analysis)Oui si critique
ImageBuild multi-stage, tag = version + SHA, push Artifact Registry—
Migration de schémaBundle EF Core ou script idempotentOui
Déploiement progressifCloud Run : révision sans trafic, puis bascule partielle (canary) ; GKE : rolling ou Cloud DeployRollback automatique sur alerte
Piège
  • Mocker DbContext : les tests passent, la vraie requête SQL échoue. Tester l'accès aux données contre une vraie base (Testcontainers).
  • Base de données en mémoire EF (UseInMemoryDatabase) : pas de transactions, pas de contraintes ; déconseillée par l'équipe EF elle-même pour les tests.
  • Tests dépendants de l'heure : injecter TimeProvider (.NET 8+) et utiliser FakeTimeProvider.

À retenir

  • Beaucoup de tests unitaires sur le domaine, des tests d'intégration sur vraie base, des tests d'architecture.
  • Versions centralisées, SDK figé, avertissements bloquants.
  • Déploiement progressif avec rollback, jamais de bascule totale à l'aveugle.

Un projet .NET à cadrer ou à reprendre ?

API REST documentées et testées, applications ASP.NET MVC (y compris VB.NET), migration de services anciens vers une architecture claire : parlons de votre code.