Tests, concurrence et qualité
Se connecter pour s'inscrireVotre progression
Syllabus
-
-
Objectifs
-
Validations de compétences
-
Public cible
Aucun public cible spécifié pour ce module.
-
Besoins techniques
-
Prérequis
- Aucun prérequis
-
Niveau
-
Historique du module
Aucune action enregistrée
Tests, concurrence et qualité
-
Ce qui se teste, et ce qui ne se teste pas
Le langage rend le test unitaire simple, parce que le code se décompose en méthodes pures sans état caché. Cette simplicité a un revers : il est tentant de tester des détails d'implementation, ce qui produit une suite lente et fragile sans protéger quoi que ce soit.
La hiérarchie, du plus rapide au plus lent
- unitaire : une méthode, sans base ni reseau. Millisecondes, doit couvrir les cas limites et les règles de gestion ;
- d'intégration : avec la base réelle, dans un conteneur éphémère. Plus lents, mais ce sont eux qui détectent les défauts de transaction et de contrainte, qu'aucun test unitaire ne peut voir ;
- de bout en bout : peu de scénarios critiques. À traiter comme une vérification ponctuelle, pas comme une couverture.
Le critère de décision
La question n'est jamais « faut-il tester ça ? » mais « quel comportement doit rester vrai, et comment saurai-je qu'il est rompu ? »
- on teste les règles de gestion : elles portent le risque métier, et une régression y coûte cher ;
- on teste les cas limites : liste vide, dépassement de capacité, nullité, valeurs négatives. Les tests qui n'utilisent que des cas nominaux ne passent jamais par le chemin du bug ;
- on ne teste pas un accesseur ni une propriété calculée : le test reproduirait l'implémentation, et échouerait à chaque refonte légitime.
Test vérifié : si le comportement change et que le test échoue, il fait son travail ; s'il échoue pour une raison sans rapport avec le comportement, il coûte.
-
Concurrence : le domaine qui produit le pire bug
Le runtime asynchrone rend l'accès concurrent naturel : deux requêtes peuvent s'exécuter entrelacées dans le même processus. Un code écrit pour un seul utilisateur à la fois fonctionne en développement et échoue en production.
Trois pièges, par ordre de fréquence
- l'état partagé entre requêtes : un service à portée d'application qui conserve une propriété par utilisateur. Les deux utilisateurs se contaminent. Invisible en recette à un seul compte ;
- la collection modifiée en lecture : l'accès à une liste ordinaire pendant qu'on y ajoute produit une exception ou un résultat faux, de façon imprévisible ;
- l'accès à une base de donnees non partagee pour une opération longue : un objet de contexte Entity Framework n'est pas thread-safe, et le partager entre tâches produit des corruptions de suivi d'entités quasi impossibles à diagnostiquer.
La règle de portée, à retenir telle quelle
Par défaut, un service doit être avec portée — une instance par requête. C'est la seule portée qui garantit l'isolation entre utilisateurs. La portée d'application ne se justifie que pour un service explicitement sans état, ce qui se vérifie en relisant ses champs.
Comment trouver un bug de concurrence avant la production
- tester avec plusieurs utilisateurs en parallèle, sur les mêmes données : c'est le seul moyen de reproduire un mélange d'état ;
- chercher les propriétés mutables des services à portée d'application ;
- vérifier que chaque unité de travail est confine à une requête, jamais partagée ni mise en champ statique.
Un test de concurrence n'est pas déterministe, et c'est normal : il ne prouve pas l'absence de bug, il prouve qu'un scénario précis échoue. Un test qui passe une fois sur cent n'est pas rassurant, il est informatif.