Tester Event Sourcing : éviter les pièges courants

Découvrez les meilleures pratiques pour tester efficacement vos systèmes Event Sourcing. Évitez les erreurs classiques et gagnez en confiance dans vos tests grâce à des stratégies éprouvées par des...

Olivier Dupuy
05 août 2025

7

Vues

0

Commentaires

3

Min de lecture

L'Event Sourcing est un pattern architectural puissant qui gagne en popularité dans l'écosystème .NET, particulièrement pour les applications modernes basées sur les microservices. Cependant, tester efficacement une application utilisant l'Event Sourcing peut s'avérer complexe et présenter de nombreux défis. Dans cet article, nous allons explorer les meilleures pratiques et les pièges courants à éviter lors des tests d'applications Event Sourcing en C#.

Les fondamentaux de l'Event Sourcing en .NET

Avant d'aborder les tests, rappelons les concepts clés de l'Event Sourcing :

  • Les événements sont immuables et représentent des faits passés
  • L'état actuel est reconstruit en rejouant la séquence d'événements
  • Les événements sont stockés dans un Event Store

Structure de base d'un événement


public abstract class DomainEvent
{
    public Guid Id { get; }
    public DateTime Timestamp { get; }
    public int Version { get; }

protected DomainEvent() { Id = Guid.NewGuid(); Timestamp = DateTime.UtcNow; } }

public class OrderCreatedEvent : DomainEvent { public Guid OrderId { get; } public decimal Amount { get; }

public OrderCreatedEvent(Guid orderId, decimal amount) { OrderId = orderId; Amount = amount; } }

Les pièges courants dans les tests

1. Dépendance temporelle

Un piège fréquent est de se fier à DateTime.Now dans les tests. Utilisez plutôt une abstraction du temps :


public interface ISystemClock
{
    DateTime UtcNow { get; }
}

public class SystemClock : ISystemClock { public DateTime UtcNow => DateTime.UtcNow; }

// Pour les tests public class FakeSystemClock : ISystemClock { private DateTime _now; public FakeSystemClock(DateTime initialTime) { _now = initialTime; }

public DateTime UtcNow => _now; public void Advance(TimeSpan duration) { _now = _now.Add(duration); } }

2. Tests d'intégration avec l'Event Store

Utilisez un Event Store en mémoire pour les tests :


public class InMemoryEventStore : IEventStore
{
    private readonly Dictionary> _events 
        = new Dictionary>();

public async Task SaveEvents(Guid aggregateId, IEnumerable events, int expectedVersion) { if (!_events.ContainsKey(aggregateId)) { _events.Add(aggregateId, new List()); }

foreach (var @event in events) { _events[aggregateId].Add(@event); } }

public async Task> GetEvents(Guid aggregateId) { if (!_events.ContainsKey(aggregateId)) { throw new AggregateNotFoundException(aggregateId); }

return _events[aggregateId]; } }

Bonnes pratiques de test

1. Given-When-Then Pattern


[Fact]
public async Task CreateOrder_WithValidData_ShouldEmitOrderCreatedEvent()
{
    // Given
    var orderId = Guid.NewGuid();
    var handler = new CreateOrderHandler(_eventStore);
    var command = new CreateOrderCommand(orderId, 100m);

// When await handler.Handle(command);

// Then var events = await _eventStore.GetEvents(orderId); var orderCreatedEvent = events.Single() as OrderCreatedEvent; Assert.NotNull(orderCreatedEvent); Assert.Equal(100m, orderCreatedEvent.Amount); }

2. Test Fixtures réutilisables


public class OrderTestFixture : IDisposable
{
    public IEventStore EventStore { get; }
    public ISystemClock SystemClock { get; }

public OrderTestFixture() { EventStore = new InMemoryEventStore(); SystemClock = new FakeSystemClock(new DateTime(2024, 1, 1)); }

public void Dispose() { // Nettoyage si nécessaire } }

Gestion des cas particuliers

1. Concurrence et versions


[Fact]
public async Task SaveEvents_WithConcurrentModification_ShouldThrowException()
{
    // Arrange
    var aggregateId = Guid.NewGuid();
    var eventStore = new InMemoryEventStore();
    
    await eventStore.SaveEvents(aggregateId, 
        new[] { new OrderCreatedEvent(aggregateId, 100m) }, 0);

// Act & Assert await Assert.ThrowsAsync(() => eventStore.SaveEvents(aggregateId, new[] { new OrderModifiedEvent(aggregateId) }, 0)); }

2. Snapshots dans les tests


public class OrderSnapshot
{
    public Guid Id { get; set; }
    public decimal Amount { get; set; }
    public int Version { get; set; }
}

[Fact] public async Task LoadFromSnapshot_ShouldRestoreCorrectState() { // Arrange var snapshotStore = new InMemorySnapshotStore(); var orderId = Guid.NewGuid(); var snapshot = new OrderSnapshot { Id = orderId, Amount = 100m, Version = 1 }; await snapshotStore.Save(snapshot);

// Act var order = await Order.LoadFromSnapshot(snapshot, _eventStore);

// Assert Assert.Equal(100m, order.Amount); Assert.Equal(1, order.Version); }

Optimisation des performances des tests

Pour maintenir des tests performants :

  • Utilisez des collections de tests parallélisables
  • Évitez les dépendances externes dans les tests unitaires
  • Implémentez un mécanisme de cache pour les agrégats fréquemment utilisés


[Collection("Parallel")]
public class ParallelOrderTests
{
    private readonly IEventStore _eventStore;
    private readonly ISystemClock _clock;

public ParallelOrderTests() { _eventStore = new InMemoryEventStore(); _clock = new FakeSystemClock(DateTime.UtcNow); }

// Tests... }

Conclusion

Tester efficacement une application Event Sourcing requiert une approche méthodique et la compréhension des pièges courants. Les points clés à retenir :

  • Utilisez des abstractions pour le temps et les dépendances externes
  • Implémentez des stores en mémoire pour les tests
  • Suivez le pattern Given-When-Then
  • Gérez correctement la concurrence et les versions
  • Optimisez les performances des tests

En suivant ces bonnes pratiques, vous pourrez construire une suite de tests robuste et maintenable pour vos applications Event Sourcing en .NET.

Partager cet article
42
12

Commentaires (0)

Rejoignez la discussion

Connectez-vous pour partager votre avis et échanger avec la communauté

Première discussion

Soyez le premier à partager votre avis sur cet article !

À propos de l'auteur
Olivier Dupuy

Développeur passionné et contributeur actif de la communauté technique.

Profil
Articles similaires
Navigation rapide
Commentaires (0)