-
Tester toujours la data, jamais les objets. Un test valide opère sur structures de données brutes (components, chunks de composants, tables), pas sur des surcouches orientées objets.
-
Isolation stricte des systèmes. Un système est une transformation
Data_in → Data_out. Les tests vérifient la transformation, pas l’environnement. -
Exécution déterministe. Aucun test ne doit dépendre d’un timer, de MonoGame, du rendu, d’un scheduler non déterministe, ou d’une API système.
-
Zéro allocation dans les tests système-critiques. Les tests détectent indirectement les allocations parasites via la structure des API :
- Systèmes stateless
- Collections pré-allouées
- Pas de LINQ, pas de yield, pas de boxing
-
Tests orientés pipeline. Chaque test valide un segment d’un pipeline ECS (lecture → processing → écriture), jamais le pipeline complet en une fois.
-
Red : Écrire un test échouant qui décrit une transformation de données attendue. Pas de mocks complexes. Pas d’objets. Juste des buffers et des components.
-
Green : Implémenter le minimum de logique fonctionnelle pour faire passer la transformation. Priorité : accès séquentiels, aucun branchement inutile.
-
Refactor : Nettoyer les accès mémoire, réduire les branches, éliminer les allocations, aligner les structures. Tant que les tests restent strictement déterministes.
Un test unitaire valide la logique d’un unique système ECS :
- Input : un
World, des entities, des components initiaux - Process : appel
system.Update() - Output : validation des components modifiés
Contraintes :
- Pas de MonoGame
- Aucun accès API externe
- Pas d’interaction avec un autre système
- Temps d’exécution < 1 ms
Ici on teste une séquence ordonnée de systèmes, jamais tout l’écosystème.
Exemple :
InputSystem → MovementSystem → CollisionSystem
On valide :
- cohérence des données entre étapes
- invariants mémoire (pas d’écrasement illégal, pas de sortie de range)
- comportement déterministe entre runs identiques
Chaque test crée un World minimal, sans dépendances, sans services mono.
var world = new World();
var entity = world.CreateEntity();
entity.Set(new Position { X = 0, Y = 0 });
entity.Set(new Velocity { X = 1, Y = 0 });Règle : Tout ce qui n’est pas utilisé par le système testé est exclu.
Un système doit être construit uniquement via sa signature de production.
var system = new MovementSystem(world);
system.Update(deltaTime);Interdit :
- passer un EntitySet manuellement
- injecter des mocks
- altérer le flux normal de construction du système
ref var pos = ref entity.Get<Position>();
Assert.Equal(1, pos.X);Pas de wrappers, pas de helpers, pas de méthodes « utilitaires » cachant l’accès aux données.
En DOD, le mocking est limité aux interfaces contraintes par le runtime.
Uniquement pour les systèmes consommant :
- input clavier
- input souris
- horloge
- filesystem
Règle : Wrap obligatoire. Le test injecte une version déterministe d’un wrapper (pas de dépendance OS/MonoGame).
- Mocker un système ECS.
- Mocker un component.
- Mocker la logique interne d’un pipeline.
RPG/
├── Components/
│ └── Position.cs
│ └── Velocity.cs
├── Systems/
│ └── MovementSystem.cs
└── Tests/
├── Components/
│ └── PositionTests.cs
└── Systems/
└── MovementSystemTests.cs
Règles :
- Chaque fichier de production → un fichier de test miroir
- Namespace miroir :
RPG.Tests.Systems
Update_WhenVelocityApplied_PositionMoves
Format :
Transformation_Condition_Resultat
Valident le caractère POD-like des components.
Assert.Equal(sizeof(float) * 2, Unsafe.SizeOf<Position>());Détecte :
- padding non désiré
- champs parasites
- struct non blittable
Vérifier qu’un système :
- n’écrit qu’aux components qu’il possède
- ne lit pas hors scope
- ne crée/détruit pas d’entities non prévues
Vérifier que les conditions restent scalaires :
- aucune dépendance runtime superflue
- branches prévisibles
- aucune complexité cachée
(Souvent indirect via la structure de code, pas instrumentation.)
❌ Tester les appels méthodes internes ❌ Tester les systèmes via des états objets (OO) ❌ Mettre des mocks partout ❌ Laisser un système dépendre d’un autre ❌ Valider du comportement gameplay hors de son pipeline ECS ❌ Utiliser LINQ dans les tests ou la production testée
✔ Tester uniquement les données et leur transformation ✔ Mesurer toujours le chemin data → data ✔ Traiter les systèmes comme des passeurs de flux
Si un système n’existe pas encore :
- utiliser
Skip - ne pas créer de stub vide
- ne pas adapter la prod aux tests, jamais