web-dev-qa-db-fra.com

Impossible de saisir la différence entre Freeze / Inject / Register

Avant de commencer, je suis un grand fan d'AutoFixture, je suis toujours dans la courbe d'apprendre à utiliser l'outil. Merci donc d'avoir développé Autofixture Mr Ploeh et tous les contributeurs.

Commençons donc par ma question.

Selon AutoFixture/AutoMoq ignore l'instance injectée/la maquette gelée

La partie intéressante du lien ci-dessus reçoit ce code

Mock<ISettings> settingsMock = new Mock<ISettings>();
settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);

ISettings settings = settingsMock.Object;
fixture.Inject(settings);

À laquelle Mark répond, il peut être réécrit

fixture.Freeze<Mock<ISettings>>()
       .Setup(s => s.Get(settingKey)).Returns(xmlString);

Il ressemble à un sucre syntaxique, en utilisant la méthode Freeze est un moyen d'écrire dans une interface fluide la création de la maquette, la configuration et l'injection dans le conteneur de montage automatique.

Après avoir fait quelques recherches sur le Web, il y a en fait une différence fonctionnelle entre Freeze et Inject. J'ai trouvé cette question: https://github.com/AutoFixture/AutoFixture/issues/59 à laquelle pointe la réponse Comment puis-je figer une instance nulle dans AutoFixture

L'auteur du lien ci-dessus décrit la méthode Freeze comme suit:

En interne, Freeze crée une instance du type demandé (par exemple IPayPalConfiguration), puis l'injecte pour qu'il renvoie toujours cette instance lorsque vous la demandez à nouveau.

Je comprends que lorsque nous faisons

var customer = fixture.Freeze<Order>();

il utilisera toujours la même instance de commande chaque fois que notre code demande un type de commande. Mais que se passe-t-il si je spécifie dans le constructeur Freeze que je veux qu'il utilise une instance spécifique?

Voici un petit exemple de code:

[Fact]
public void MethodeName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    fixture.Freeze<OrderLine>(new OrderLine("Foo"));
    var order = fixture.Create<Order>();
}

public class Order
{
    private readonly OrderLine _line;

    public Order(OrderLine line)
    {
        _line = line;
    }
}
public class OrderLine
{
    private readonly string _name;

    public OrderLine(string name)
    {
        _name = name;
    }
}

Le nom de OrderLine ne devrait-il pas être égal à "Foo" au lieu de namefe48163a-d5a0-49a5-b349-7b11ba5f804b? La documentation de la méthode Freeze dit:

<typeparam name="T">The type to freeze.</typeparam>
<param name="fixture">The fixture.</param>
<param name="seed">Any data that adds additional information when creating the anonymous object. Hypothetically, this value might be the value being frozen, but this is not likely.</param>

pourquoi l'auteur n'est-il pas sûr du retour de la valeur? Si je spécifie mon instance dans le constructeur de Freeze, je m'attends à ce que l'autofixture utilise cette instance?

puis

Veuillez noter que le n'est pas susceptible d'être utilisé comme valeur figée, sauf si vous l'avez personnalisé pour ce faire. Si vous souhaitez injecter une valeur spécifique dans le luminaire, vous devez utiliser la méthode à la place. "

Il semble que je doive personnaliser le paramètre de départ. Quelqu'un peut-il clarifier? La solution indiquée par la documentation est d'utiliser la méthode Inject. Et en effet, cela fonctionne dans mon exemple de code avec OrderLine.

Je cherche votre aide pour comprendre la différence entre Freeze, Inject et également Register qui, selon le code source, est simplement appelé par la méthode Inject mais il faut un lambda.

45
John

Enregistrer et injecter

Il était une fois, il n'y avait ni Inject ni Freeze; Register a gouverné le code.

À l'époque, il y avait une surcharge Register définie ainsi:

public static void Register<T>(this IFixture fixture, T item)

Cependant, il devait partager l'API avec ce proche parent:

public static void Register<T>(this IFixture fixture, Func<T> creator)

Le créateur d'AutoFixture a pensé que c'était bien, mais hélas: les utilisateurs étaient frappés de confusion. Plus grave, un utilisateur pourrait écrire:

fixture.Register(() => universe.LightUp());

mais aussi

fixture.Register(universe.LightUp);

ce qui signifie exactement la même chose, car universe.LightUp est une référence à une méthode, et correspond donc à un délégué.

Cependant, cette syntaxe ressemble à une référence de propriété, donc si LightUp avait été une propriété au lieu d'une méthode, la première surcharge serait sélectionnée par le compilateur.

Cela a causé beaucoup de confusion, donc la surcharge Register<T>(this IFixture fixture, T item) a été renommée Inject<T>(this IFixture fixture, T item).

Gel

Freeze a une histoire différente. Il y a longtemps, quand j'utilisais encore AutoFixture de manière impérative, j'ai remarqué que j'écrivais à plusieurs reprises du code comme celui-ci:

var foo = fixture.Create<Foo>();
fixture.Inject(foo);

J'ai donc décidé que c'était un concept et je l'ai nommé Freeze. La méthode Freeze n'est qu'un raccourci pour ces deux lignes de code.

Je cherche votre aide pour comprendre la différence entre Freeze, Inject et également Register qui, selon le code source, est simplement appelé par la méthode Inject mais il faut un lambda

En général, il ne devrait pas être trop difficile de faire la distinction entre Inject et Register, car leurs signatures ne se heurtent pas. Ainsi, si vous essayez d'atteindre un objectif avec l'une de ces deux méthodes et que votre code se compile, vous avez probablement choisi la bonne version.

Ce serait également le cas pour Freeze s'il n'y avait pas la surcharge utilisée dans l'OP:

[EditorBrowsable(EditorBrowsableState.Never)]
public static T Freeze<T>(this IFixture fixture, T seed)

Notez que cette surcharge a en fait EditorBrowsableState.Never, Car elle déroute toujours les gens. Cependant, malgré cela, apparemment, les gens trouvent toujours cette surcharge, donc je pense il devrait être déplacé dans AutoFixture 4 . C'est l'une de ces fonctionnalités qui existent car elle était facile à mettre en œuvre ...

47
Mark Seemann

Freeze, Inject et Register personnalisent tous l'algorithme de création.

Avec Inject et Register vous spécifiez explicitement qu'un objet doit être créé d'une manière particulière, dans votre exemple en fournissant new OrderLine("Foo") manuellement.

Avec Freeze vous ne spécifiez pas comment un objet doit être créé - vous demandez à AutoFixture de vous fournir une instance.

Au final, toutes les méthodes ci-dessus utilisent la même API de niveau inférieur:

fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());


La raison pour laquelle fixture.Freeze<OrderLine>(new OrderLine("Foo")); ne crée pas d'instance OrderLine avec la valeur de départ spécifiée est due au fait par défaut, la valeur de départ est ignorée .

Pour favoriser les valeurs de départ d'un type particulier, vous pouvez créer un SeedFavoringRelay<T>:

public class SeedFavoringRelay<T> : ISpecimenBuilder where T : class
{
    public object Create(object request, ISpecimenContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var seededRequest = request as SeededRequest;
        if (seededRequest == null || !seededRequest.Request.Equals(typeof(T)))
            return new NoSpecimen(request);

        var seed = seededRequest.Seed as T;
        if (seed == null)
            return new NoSpecimen(request);

        return seed;
    }
}

Ensuite, vous pouvez l'utiliser comme ci-dessous:

fixture.Customizations.Add(
    new SeedFavoringRelay<OrderLine>());

fixture.Freeze<OrderLine>(new OrderLine("Foo"));
// -> Now fixture.Create<Order>() creates an Order with OrderLine's Name = "Foo".
15
Nikos Baxevanis

J'ai modifié votre test (qui n'affirme actuellement rien, BTW) et si vous passez par son exécution, vous verrez un OrderLine avec "Foo" car sa valeur de membre privé _line est injectée dans Order.

J'ai eu une autre version du test où j'ai ajouté des propriétés en lecture seule pour OrderLine dans Order et Name dans OrderLine afin que vous puissiez faire des assertions sur ces objets, mais ce n'est ni ici ni là.

Ce test configure le luminaire en utilisant directement la méthode FromFactory, ce qui peut parfois être utile:

[Fact]
public void MethodName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    const string expected = "Foo";
    fixture.Customize<OrderLine>(o => o
        .FromFactory(() =>
            new OrderLine(expected)));
    var order = fixture.Create<Order>();
}
1
Jeff