Comment créer un générateur en C # pour un objet dont les propriétés sont des types de référence?

StackOverflow https://stackoverflow.com/questions/1402975

Question

J'ai commencé à construire un générateur afin de pouvoir facilement créer des données de test pour les tests unitaires que j'écris.

La structure de base du constructeur est la suivante:

public class MyClassBuilder
{
    public int id = 0; //setting the default value here

    //allows MyClass to be built with a specific id
    public MyClassBuilder WithId(int id)
    {
        this.id = id;
        return this;
    }

    public MyClass Build()
    {
        return new MyClass(id);
    }
}

L'utilisation de ce modèle devient alors:

MyClass mc = new MyClassBuilder().WithId(5).Build();

Je suis à l'aise avec ça ... mais j'ai des questions, c'est quand MyClass a une propriété qui n'est pas triviale ... Je suis un peu incertain sur la façon de construire avec une valeur par défaut.

public class MyClassBuilder
{
    public int id = 0; //setting the default value here

    //do I construct the default value using a MySecondClassBuilder?
    public MySecondClass mySecondClass;

    //allows MyClass to be built with a specific id
    public MyClassBuilder WithId(int id)
    {
        this.id = id;
        return this;
    }

    public MyClassBuilder WithMySecondClass(MySecondClass mySecondClass)
    {
        this.mySecondClass = mySecondClass;
    }

    public MyClass Build()
    {
        return new MyClass(id);
    }
}

Mon hypothèse est que je créerais un générateur pour MySecondClass et l'utiliserais pour créer l'implémentation par défaut.

Quelqu'un peut-il confirmer que mon hypothèse est correcte et constitue la meilleure pratique?

Je suis actuellement en train de tester mon hypothèse, mais je pensais documenter cette idée sur StackOverflow puisque ce sont les seuls exemples de modèles de générateur que je pouvais trouver à l'aide de Google uniquement des propriétés jamais construites qui sont des types de valeur et non types de référence.

Était-ce utile?

La solution

Comme dans la plupart des cas, cela dépend.

Si vous voulez une sémantique de référence dans le champ mySecondClass (c'est-à-dire: vous voulez enregistrer une référence dans une autre classe), alors c'est exactement comme votre entier. Avoir la valeur par défaut null pourrait être parfaitement acceptable.

Cependant, si vous voulez toujours garantir une valeur, vous devez créer ou simplement construire un nouvel objet pour votre deuxième classe.

En outre, si vous souhaitez cloner au lieu de copies de référence, vous devez créer un nouvel objet et l'assigner à la référence.

Cela étant dit, dans de nombreux cas, les constructeurs traditionnels sont plus appropriés que d'essayer de créer une interface de style de construction fluide. Il est très facile de "gâcher" une interface fluide pour la construction de ce type, car vous vous fiez davantage à l'utilisateur qu'à un constructeur traditionnel.

Par exemple, dans votre code, que se passe-t-il si l'utilisateur n'appelle jamais Build? Il retourne toujours quelque chose (puisque chacune des méthodes retourne un objet), mais est-ce valide? Je ne serais pas sûr en tant que consommateur de votre classe. Les constructeurs traditionnels avec des surcharges, en revanche, me montrent exactement ce qui est valide et ce qui n’est pas valide, et sont appliqués à la compilation.

Autres conseils

Une fois que j'ai un objet complexe pour lequel je veux un mannequin à utiliser dans les tests unitaires, je me tourne généralement vers un cadre moqueur / simulateur / isolement. En C #, ma préférence va à Moq, mais il en existe plusieurs, comme Rhino Mocks, Typemock, etc.

L'avantage qu'ils ont est qu'ils auront généralement la capacité d'extorquer toutes les propriétés existantes dans le type d'origine avec des valeurs par défaut et des objets vides, sans que vous ayez à faire autre chose qu'une instruction à une seule ligne pour créer le faux objet. Dans le même temps, vous pouvez facilement configurer l'objet pour qu'il renvoie les valeurs souhaitées pour celles des propriétés importantes pour votre test. De plus, la plupart de ces frameworks prennent en charge la simulation de manière récursive, de sorte que les objets complexes à l'intérieur de votre type soient également falsifiés, etc. ".

Il y a des raisons pour lesquelles on pourrait ne pas vouloir utiliser un cadre séparé, auquel cas je pense que la suggestion de Reed Copsey serait la manière manuelle de le faire.

S'il n'y a aucune raison d'éviter l'ajout d'un framework moqueur (ce qui ne constituerait qu'une dépendance de vos projets de test, si vous divisez les tests), je le recommande vivement pour les types complexes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top