Tous mes tests passent, mais ça plante en prod
Le scénario qui devrait être impossible
Couverture de tests à 85%. Tous au vert. Déploiement en production. Et puis :
- L’API retourne des erreurs 500
- Les données ne sont pas sauvegardées en base
- La sérialisation JSON casse les dates
- L’injection de dépendances n’est pas configurée correctement
Comment est-ce possible ? Les tests passent.
Pourquoi : les tests unitaires testent l’isolation, pas l’assemblage
Un test unitaire vérifie qu’un composant fonctionne seul. Il remplace toutes les dépendances par des doublures (stubs, fakes) et vérifie le comportement en isolation.
C’est sa force. C’est aussi sa limite.
Test unitaire du UserRepository → passe ✅ (avec FakeDbContext)
Test unitaire du UserService → passe ✅ (avec FakeUserRepository)
Test unitaire du UserController → passe ✅ (avec FakeUserService)
En production : le Controller parle au Service qui parle au Repository
qui parle à SQL Server... et quelque chose dans cette chaîne est cassé.
Les composants fonctionnent chacun de leur côté. Ce qui est cassé, c’est le câblage entre eux.
Les bugs que les tests unitaires ne voient pas
Mapping ORM incorrect Le repository passe les tests avec une base en mémoire. En production, la migration SQL n’a pas été jouée, ou le mapping d’une colonne est mauvais.
Sérialisation JSON
Le serializer par défaut d’ASP.NET sérialise DateTime différemment selon les cultures. Les tests ne passent pas par le pipeline HTTP.
Configuration de l’injection de dépendances
On a oublié d’enregistrer IEmailSender dans le conteneur DI. Startup.cs compile. Les tests unitaires injectent directement. En production : InvalidOperationException.
Comportement de la vraie base de données
EF Core InMemoryDatabase n’exécute pas de SQL. Les contraintes de clé étrangère, les transactions, les comportements de concurrence — tout ça n’existe pas en mémoire.
La solution : monter dans la pyramide
La pyramide des tests propose trois niveaux :
/\
/e2e\ ← Peu nombreux, coûteux, vérifient l'ensemble
/------\
/ intégr \ ← Vérifient les connexions entre composants
/----------\
/ unitaires \ ← Nombreux, rapides, vérifient l'isolation
/--------------\
Les tests d’intégration comblent exactement le gap : ils vérifient qu’un composant fonctionne avec une vraie dépendance externe — une vraie base de données, un vrai pipeline HTTP, un vrai serializer.
// Test d'intégration : teste le repository avec une vraie BDD (SQLite in-memory)
public class UserRepositoryIntegrationTests : IDisposable
{
private readonly AppDbContext _context;
public UserRepositoryIntegrationTests()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite("DataSource=:memory:")
.Options;
_context = new AppDbContext(options);
_context.Database.EnsureCreated();
}
[Fact]
public void Add_PersistsUserToDatabase()
{
var repo = new UserRepository(_context);
repo.Add(new User { Name = "Alice", Email = "alice@test.com" });
var saved = _context.Users.Single();
Assert.Equal("Alice", saved.Name);
}
public void Dispose() => _context.Dispose();
}
Ce test utilise une vraie base SQLite, pas une liste en mémoire. Il vérifie la vraie requête SQL, le vrai mapping EF Core, les vraies contraintes.
Choisir le bon niveau
La question à se poser n’est pas “est-ce que je dois faire des tests d’intégration ?” mais “quel est le niveau minimum pour vérifier ce comportement ?”
| Je veux vérifier… | Niveau approprié |
|---|---|
| Une règle de calcul métier | Unitaire |
| Une validation de domaine | Unitaire |
| Le mapping ORM et les requêtes SQL | Intégration |
| Le routing et la sérialisation de l’API | Intégration |
| Le parcours utilisateur complet | E2E |
| Que l’application démarre | E2E (smoke test) |
Plus on monte dans la pyramide, plus c’est coûteux. On monte uniquement quand le niveau inférieur ne suffit pas.
En résumé
- Les tests unitaires vérifient les composants en isolation — leur valeur est énorme, mais ils ont une limite
- Ils ne voient pas les problèmes de câblage entre composants : ORM, DI, sérialisation, SQL
- Les tests d’intégration comblent ce gap en testant une connexion à la fois avec de vraies dépendances
- La pyramide n’est pas un dogme — c’est un outil de décision : quel est le niveau minimum pour vérifier ce comportement ?
Quand vos tests passent mais que la prod plante, ne cherchez pas un bug dans votre code métier. Cherchez ce que vos tests unitaires n’ont pas pu voir.
La pyramide des tests, Testcontainers, WebApplicationFactory et les tests d’intégration ASP.NET sont au programme de mes formations Tests & Validation au CNAM Strasbourg.