JustDummies

Juste des dummies, mais redoutablement efficaces.

Sections de la documentation

JustDummies.Cli

dum écrit le générateur de dummy pour l’un de vos types, une fois, sous forme de code ordinaire que vous possédez et modifiez. Ce n’est pas un générateur de source et il ne s’exécute pas à la compilation : il lit votre compilation, émet un fichier, et s’efface.

Installation

dotnet tool install --global JustDummies.Cli

Le paquet installe une seule commande, dum. Contrairement aux trois bibliothèques, vous ne le référencez jamais depuis un projet : c’est un outil, pas une dépendance.

Ce qu’il produit

Lancez-le depuis votre projet de test : c’est là que le fichier a sa place, et là que le type est atteignable.

$ dum generate Order

Analyzing Shop.Domain.Order
  constructor Order(OrderReference, Customer, int, OrderStatus, IReadOnlyList<string>, DateTime)

  reference  OrderReference         new AnyOrderReference()              AnyX
  customer   Customer               new AnyCustomer()                    AnyX
  quantity   int                    Any.Int32().Positive()               guard
  status     OrderStatus            Any.Enum<OrderStatus>()
  tags       IReadOnlyList<string>  Any.ListOf(Any.String().NonEmpty())
  placedAt   DateTime               Any.DateTime()

 AnyOrder.cs — 6 of 6 parameters inferred.

AnyOrder.cs est une partial class implémentant IAny<Order>, avec une méthode With… par paramètre du constructeur. Il vous appartient dès cet instant : lisez-le, modifiez-le, commitez-le.

Ce que dit la dernière colonne

C’est tout l’intérêt du récapitulatif, pas une décoration — elle sépare ce qui a été inféré de ce qui a été deviné :

MotSignification
(vide)directement issu de la table de base pour ce type
guardun guard du constructeur l’a resserré (quantity <= 0.Positive())
AnyXtiré par le generator que ce type possède
TODOrien n’a pu être inféré ; le fichier nomme ce qu’il reste à faire
to verifyun générateur a bien été inféré, mais quelque chose près de ce paramètre n’a pas pu être lu — vérifiez-le
unread guardsce « quelque chose » : une garde que l’outil ne reconnaît pas, un helper dans lequel il ne voit pas, ou une garde qu’il lit sans pouvoir la situer — sous une écriture du paramètre, ou sous quelque chose qui décide si elle s’exécute
constraint unavailableune garde a été comprise, et ce générateur n’a aucun membre pour l’exprimer
no sourcele type vient d’un package, il n’y avait donc aucun corps de constructeur à lire
unavailablele générateur existe dans JustDummies, mais pas dans l’asset que votre projet résout

Un TODO n’est pas un échec. L’outil émet un identifiant qui n’existe pas, si bien que votre propre build signale ce qui n’a pas pu être inféré, à la ligne exacte, avec le type sous la main (ADR-0060 ouvre un nouvel onglet). Un générateur qui tirerait discrètement une valeur plausible à cet endroit serait bien pire.

to verify fonctionne pareil, et pour la même raison. Là où votre constructeur délègue sa validation à un helper, garde dans une forme que l’outil n’analyse pas, ou garde à un endroit dont il ne peut pas répondre — sous une écriture du paramètre, ou sous quelque chose qui décide si la garde s’exécute —, il ne peut pas promettre que la recette inférée honore votre véritable invariant — alors il écrit cette recette comme base de travail et ajoute au-dessus une ligne qui ne compile pas :

private static IAny<string> AnyValidValue() {
    // TODO(dum): 'string value' may be guarded by something dum could not read (§9).
    //   This is dum's best generator for the type; verify it honours the real invariant,
    //   or replace it, then delete the line below.
    _ = TODO_verify_the_generator_for_value;

    return Any.String().NonEmpty();
}

Gardez la recette ou remplacez-la, supprimez cette seule ligne, et c’est réglé. L’alternative — un fichier qui compile et tire une valeur que votre constructeur rejette lors d’une exécution ultérieure — est l’échec qui coûte le plus cher, parce qu’il surgit loin de sa cause (ADR-0083 ouvre un nouvel onglet).

À travers un graphe d’agrégats

Un type de domaine est tiré par le generator qu’il possède — new AnyOrderReference() — là où vit la recette de ce type. Rien ne la redérive à chaque site qui le compose, donc aucun fichier ne porte sa propre copie d’un invariant susceptible de dériver du constructeur qu’il décrit (ADR-0089 ouvre un nouvel onglet).

Ce nom est écrit que vous l’ayez scaffoldé ou non, si bien que scaffolder un agrégat d’abord vous donne un fichier qui nomme ce qui lui manque encore :

error CS0246: The type or namespace name 'AnyOrderReference' could not be found

Soit la liste de travail, à la ligne qui en a besoin :

dum generate OrderReference
dum generate Order --force

Le récapitulatif ne le répète pas — un fichier qui ne build pas n’est pas un silence. La seule forme composée qu’il laisse à un TODO est un type générique, parce que AnyRepository nommerait aussi mal Repository<Order> que Repository<Line>.

L’atteindre comme Any.Order()

new AnyOrder() est la façon d’atteindre le generator, et elle marche toujours. Si vous préférez que les deux moitiés d’un bloc d’arrangement se lisent pareil — Any.Int32() sur une ligne et Any.Order() sur la suivante — demandez un point d’entrée :

dum generate Order --entry-point any

Cela écrit un second fichier, AnyOrder.Entry.cs, à côté du generator :

Order order = Any.Order().WithStatus(OrderStatus.Pending).Generate();

Il utilise un membre d’extension C# 14, donc le projet doit compiler en C# 14 ; en deçà, dum le dit et s’arrête plutôt que de vous donner discrètement autre chose. Si vous ne pouvez pas relever la version de langage — ou si vous préférez que la racine soit la vôtre — nommez-en une :

dum generate Order --entry-point static:Dummies    # Dummies.Order()

Cette forme n’exige aucun C# 14. La racine est partial et chaque type apporte son propre fichier, donc dum generate Order Customer Invoice --entry-point static:Dummies vous donne Dummies.Order(), Dummies.Customer() et Dummies.Invoice() sans qu’aucun fichier soit écrit deux fois.

Par défaut le point d’entrée est déclaré à côté du generator, ce qui ne coûte aucun import à vos tests. --entry-point-namespace déplace ce fichier — et lui seul — pour qu’une racine unique rassemble des types de plusieurs namespaces :

dum generate Order --entry-point static:Dummies --entry-point-namespace Shop.Tests.Dummies

Le generator, lui, ne bouge pas, et AnyOrder.cs est identique octet pour octet quelle que soit la valeur demandée. Une racine nommée Any est refusée : une classe statique de ce nom dans votre propre projet masquerait JustDummies.Any pour tout son namespace, et Any.Int32() cesserait de compiler — ce que --entry-point any existe précisément pour éviter. Décision : ADR-0070 ouvre un nouvel onglet.

Rendre compte à un script

dum rend compte à un lecteur par défaut. --format json rend compte à un script à la place — un document JSON sur stdout, et rien d’autre dessus :

dum generate Order Customer Invoice --format json > report.json

Il existe parce que le code de sortie ne peut pas tout dire. Un fichier écrit avec des TODO ouverts est un succès — c’est tout le design — donc 0 se lit pareil que tous les paramètres aient résolu ou qu’un tiers d’entre eux non. Le rapport, lui, le dit :

{
  "summary": { "scaffolded": 3, "failed": 0, "openParameters": 2, "parametersToVerify": 1 }
}

Les deux compteurs sont distincts parce qu’ils décrivent des états différents : un paramètre ouvert n’a aucune expression, un paramètre à vérifier en a une et réclame quand même votre regard. Chaque ligne de paramètre énonce les deux — "resolved" et "requiresVerification" — de sorte que les compteurs se vérifient contre les lignes au lieu d’être crus sur parole.

Chaque résultat porte le type, les fichiers écrits et où ils sont allés, chaque paramètre avec son expression et sa provenance, le point d’entrée s’il y en a un, et les avertissements. Une exécution arrêtée avant son premier scaffold produit un document elle aussi, dont le refusal dit pourquoi — de sorte que stdout porte toujours exactement un document. Tout ce qui est écrit pour une personne continue d’aller sur stderr, ce qui laisse à 2>/dev/null un tuyau propre.

Les codes de sortie sont inchangés : ceci ajoute un canal plutôt que d’en redéfinir un. Décision : ADR-0071 ouvre un nouvel onglet.

Fixer les défauts une fois

Les options qui décrivent votre projet plutôt que cette invocation ont leur place dans un dum.json à côté de votre fichier projet, commité avec lui :

{ "output": "./Dummies", "entryPoint": "static:Dummies", "entryPointNamespace": "Shop.Tests.Dummies" }

dum generate Order vous donne alors ce que la longue ligne de commande aurait donné. Cinq clés sont lues — output, namespace, entryPoint, entryPointNamespace, format — et la ligne de commande l’emporte toujours sur chacune, de sorte qu’une invocation peut différer sans éditer le fichier. --force et --dry-run n’en font pas partie : elles disent à quoi sert cette exécution, pas ce qu’est le projet.

Une clé non lue est refusée, en la nommant — un défaut que vous croyez en vigueur et qui ne l’est pas est pire que pas de fichier du tout. Un output relatif est résolu depuis le dossier du projet, donc il veut dire la même chose d’où que vous lanciez l’outil. Décision : ADR-0072 ouvre un nouvel onglet.

Options

OptionDéfautSignification
--project <chemin>l’unique *.csproj du répertoire courantprojet dont la compilation est analysée
--output <dossier>le répertoire courantoù le fichier est écrit
--namespace <ns>le namespace du type visénamespace du type émis
--entry-point <v>noneémet en plus un point d’entrée : none, static:<Name> ou any
--entry-point-namespace <ns>le namespace du type émisnamespace du seul fichier de point d’entrée
--forceinactifécrase un fichier existant — les deux fichiers, quand il y en a deux
--dry-runinactifimprime le fichier sur la sortie standard ; n’écrit rien
--format <f>humancomment l’exécution rend compte : human ou json

dum generate Order Customer Invoice en scaffolde plusieurs. Ils sont traités indépendamment, et le code de sortie est le pire d’entre eux : 0 un fichier écrit (TODO compris), 1 un scaffolding qui a échoué, 2 une instruction que l’outil n’a pas pu lire — une ligne de commande, ou un dum.json.

Il ne référence jamais JustDummies

L’outil résout chaque symbole de la bibliothèque par son nom, contre votre compilation, et ne déclare aucune dépendance vers elle (ADR-0063 ouvre un nouvel onglet). L’outil et la bibliothèque versionnent donc indépendamment, et dum ne peut pas entraîner une montée de version de JustDummies dans votre projet. Si un générateur n’existe pas dans l’asset que vous résolvez, il le dit plutôt que d’émettre un appel qui ne compilera pas.

Prérequis

Le paquet JustDummies dans le projet analysé — sans lui rien ne peut être résolu, et dum le dit plutôt que d’émettre quoi que ce soit.

L’outil lui-même cible .NET 8 et roule vers l’avant : n’importe quel runtime plus récent que vous avez installé l’exécute.

Lire la source, ou la corriger là-bas ouvre un nouvel onglet· Repris depuis cli-v1.1.0-beta.4