yavsc/doc/testing.md
Lum cb7526de9d Blog: add test guarding the display-template fix + document test architecture
Le commit 2 a fixé la NPE du /BlogSpot/Details/{id} en passant
le display template par UserDisplayHelpers.AvatarSrc, qui
défend contre un UserName null. Ce commit complète le filet
de non-régression et pose la doc d'architecture des tests.

- ApplicationUserDisplayTemplateTests : assert que le cshtml ne
  concatène plus directement Model.UserName (ancien code fautif)
  et qu'il utilise bien le helper. Si quelqu'un revert la ligne
  4 du cshtml, les tests cassent. Les autres usages de
  Model.UserName (alt, title, asp-route-id) sont autorisés : ils
  ne sont pas la cause du 500, juste laids si null.
- doc/testing.md : vue d'ensemble de la stratégie de test
  (conventions NonRegression/Mandatory/Smoke/Controllers, EF
  in-memory via InMemoryDatabaseRoot partagé, auth stubs,
  quand ne pas écrire de test).
- src/Yavsc.Tests.Shared/README.md : détails du scaffold partagé
  (WebHostFixture + son cycle de vie et ses hooks,
  TestAuthPolicyProvider, TestTokenIssuer) et des deux
  spécialisations dans le repo
  (Yavsc.Org.Tests.WebServerFixture et
  Yavsc.Blogs.Tests.BlogsWebServerFixture).
- doc/README.md : entrée vers testing.md dans l'index.
2026-07-11 19:59:03 +01:00

3.7 KiB

Stratégie de test

Yavsc utilise xUnit (xunit.v3) avec un mix d'unitaire pur et d'intégration légère. Les projets de tests sont sous src/<projet>.Tests/ et consomment le scaffold partagé src/Yavsc.Tests.Shared/.

Vue d'ensemble

Sujet Document
Scaffold partagé (WebHostFixture, JWT de test, etc.) src/Yavsc.Tests.Shared/README.md
Convention des dossiers de tests Conventions
Driver EF Core en test EF Core en test
Stubs d'authentification et de permissions Auth et permissions

Conventions des dossiers de tests

Sous src/<projet>.Tests/, on trouve quatre dossiers de premier niveau qui classifient les tests par intention :

Dossier Usage
NonRegression/ Régressions : un bug constaté, un test qui le détecte si on le réintroduit
Mandatory/ Tests bloquants : ils doivent passer avant tout merge
Smoke/ Smoke tests HTTP rapides, montent un host léger
Controllers/ Tests unitaires des contrôleurs (mock du service, assertions sur le mapping HTTP)

Les NonRegression sont la cible par défaut quand on fixe un bug : ils doivent être rouges avant le fix, verts après, et continuer à casser si quelqu'un revert le fix. Pas de test qui passe à vide.

EF Core en test

Pour les tests qui ont besoin d'un ApplicationDbContext, on utilise UseInMemoryDatabase avec un InMemoryDatabaseRoot partagé au niveau de la fixture. Pas de SQLite, pas de Docker, pas de mock du contexte : le service testé s'exécute contre un vrai DbContext sur in-memory.

private static readonly InMemoryDatabaseRoot _dbRoot = new();

var opts = new DbContextOptionsBuilder<ApplicationDbContext>()
    .UseInMemoryDatabase("Yavsc.Org.Tests.MyFixture", _dbRoot)
    .Options;

Le InMemoryDatabaseRoot partagé est important : sans lui, EF crée un store indépendant par DbContext dans certaines configurations, et un test qui seed + read sur deux contextes voit un store vide. Le pattern est documenté dans BlogsWebServerFixture (src/Yavsc.Blogs.Tests/BlogsWebServerFixture.cs).

Limite connue : le provider in-memory ignore les Migration EF et ne respecte pas les FK sur les raw SQL (ExecuteSqlRaw). Pour tester des contraintes FK, on écrit la configuration dans OnModelCreating et on s'appuie sur le fait qu'EF la respecte à l'Add/SaveChanges. Pour tester des migrations, c'est l'environnement de staging.

Auth et permissions

L'authorization policy provider de prod est swappé contre TestAuthPolicyProvider (dans Yavsc.Tests.Shared) par les fixtures spécialisées. Les tests qui ont besoin qu'un user soit "Administrator" envoient un header X-Test-Rôle ; ceux qui veulent un user anonyme omettent le header.

Pour les tests unitaires qui n'ont pas besoin du pipeline HTTP, on stub IAuthorizationService directement (cf. BlogspotController dans Yavsc.Org.Tests/NonRegression/) pour éviter de monter un host complet.

Quand ne PAS écrire de test

Un test qui ne détecte rien n'est pas un test. Si l'invariant qu'on cherche à protéger est déjà enforced par EF, par le compilateur, ou par une couche applicative en amont, le test est du bruit. Mieux vaut :

  • Un test qui assert un comportement observable (code retour HTTP, exception typée, valeur de retour)
  • Ou pas de test, et une note dans le code

La non-régression se prouve par un test qui casse si on réintroduit le bug. Pas par un test qui passe aujourd'hui et qui continuera à passer après un revert.