JustDummies

Juste des dummies, mais redoutablement efficaces.

Sections de la documentation

Dates et heures

Cinq générateurs couvrent les types temporels. Ils partagent un vocabulaire d’ordonnancement, et deux d’entre eux ajoutent une dimension que les autres n’ont pas : la granularité, et le décalage horaire.

Les cinq générateurs

FabriqueTireDisponibilité
Any.DateTime()DateTimepartout
Any.DateTimeOffset()DateTimeOffsetpartout
Any.TimeSpan()TimeSpanpartout
Any.DateOnly()DateOnly.NET 8+
Any.TimeOnly()TimeOnly.NET 8+

Ordonnancement

Les instants s’ordonnent plutôt qu’ils ne se comparent en taille : le vocabulaire est donc temporel — After et Before plutôt que GreaterThan et LessThan :

DateTime ordered   = Any.DateTime().Between(new DateTime(2020, 1, 1), new DateTime(2025, 12, 31)).Generate();
DateTime recent    = Any.DateTime().After(new DateTime(2024, 1, 1)).Generate();
DateTime onOrAfter = Any.DateTime().AfterOrEqualTo(new DateTime(2024, 1, 1)).Generate();
DateTime past      = Any.DateTime().Before(new DateTime(2030, 1, 1)).Generate();
DateTime onOrBefore = Any.DateTime().BeforeOrEqualTo(new DateTime(2030, 1, 1)).Generate();

TimeSpan est une durée et non un instant : il conserve donc le vocabulaire numérique et ajoute la famille du signe :

TimeSpan timeout  = Any.TimeSpan().Between(TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(5)).Generate();
TimeSpan positive = Any.TimeSpan().Positive().Generate();
TimeSpan nonZero  = Any.TimeSpan().NonZero().Generate();
TimeSpan shorter  = Any.TimeSpan().LessThan(TimeSpan.FromHours(1)).Generate();

Granularité

WithGranularity aligne la valeur tirée sur une grille. C’est ce qui transforme un instant brut en un instant que votre domaine stockerait réellement :

// Minutes entières : plus de secondes ni de ticks parasites pour casser une assertion d'égalité.
DateTime appointment = Any.DateTime()
                          .Between(new DateTime(2025, 1, 1), new DateTime(2025, 12, 31))
                          .WithGranularity(TimeSpan.FromMinutes(1))
                          .Generate();

// Jours entiers.
DateTime businessDay = Any.DateTime().WithGranularity(TimeSpan.FromDays(1)).Generate();

// Une durée en secondes entières.
TimeSpan retryAfter = Any.TimeSpan().Positive().WithGranularity(TimeSpan.FromSeconds(1)).Generate();

Sans elle, un instant tiré porte une précision inférieure à la seconde, et un test qui le fait transiter par un stockage tronquant aux secondes échoue pour une raison sans rapport avec le code testé. Déclarer la granularité que votre stockage a réellement est le remède, et c’est un véritable invariant du domaine plutôt qu’un contournement.

DateOnly n’a pas de contrainte de granularité — c’est déjà un jour entier.

DateTimeOffset varie sur deux dimensions

Un DateTimeOffset est un instant et un décalage par rapport à UTC, et JustDummies fait varier les deux. C’est délibéré : un dummy portant toujours +00:00 ne trouverait jamais le code qui suppose l’heure locale (ADR-0016 ouvre un nouvel onglet).

DateTimeOffset anywhere = Any.DateTimeOffset().Generate();                       // instant et décalage varient
DateTimeOffset utc      = Any.DateTimeOffset().WithOffset(TimeSpan.Zero).Generate();
DateTimeOffset european = Any.DateTimeOffset()
                             .WithOffsetBetween(TimeSpan.FromHours(0), TimeSpan.FromHours(3))
                             .Generate();

Déclarer un décalage filtre le vivier d’instants au lieu de réécrire l’instant tiré : la paire obtenue est une paire qui existe réellement ensemble (ADR-0030 ouvre un nouvel onglet).

Le piège de DateTime.Now

Voici l’erreur qui mérite d’être nommée, car elle survit à la revue et échoue à minuit :

// Fragile : le dummy est tiré relativement à une horloge que le test ne contrôle pas.
DateTime createdAt = Any.DateTime().Before(DateTime.Now).Generate();

Le test dépend désormais du moment où il s’exécute. Pire, il n’est pas reproductible : rejouer la graine rejoue le tirage, pas l’horloge.

Épinglez un instant que le test possède, et contraignez relativement à lui :

DateTime now       = new DateTime(2025, 6, 15, 12, 0, 0, DateTimeKind.Utc);
DateTime createdAt = Any.DateTime().Before(now).AfterOrEqualTo(now.AddYears(-1)).Generate();

Appartenance et exclusion

Tout générateur temporel porte la famille habituelle du vivier et de l’exclusion :

DateTime quarterEnd = Any.DateTime()
                         .OneOf(new DateTime(2025, 3, 31), new DateTime(2025, 6, 30), new DateTime(2025, 9, 30))
                         .Generate();

DateTime notEpoch = Any.DateTime()
                       .Between(new DateTime(2020, 1, 1), new DateTime(2025, 1, 1))
                       .DifferentFrom(new DateTime(2020, 1, 1))
                       .Generate();

DateOnly et TimeOnly

Disponibles à partir de .NET 8, avec le même vocabulaire d’ordonnancement :

DateOnly dueDate = Any.DateOnly()
                      .Between(new DateOnly(2025, 1, 1), new DateOnly(2025, 12, 31))
                      .Generate();

TimeOnly openingTime = Any.TimeOnly()
                          .Between(new TimeOnly(8, 0), new TimeOnly(20, 0))
                          .WithGranularity(TimeSpan.FromMinutes(15))
                          .Generate();

Sur netstandard2.0, ces types n’existent pas, et leurs fabriques non plus. Tout le reste de cette page est disponible sur toutes les cibles.

Lire la source, ou la corriger là-bas ouvre un nouvel onglet· Repris depuis lib-v1.0.0-preview.6