Autofixture: La création d'une instance personnalisée échoue sur les références circulaires

Créé le 13 janv. 2018  ·  5Commentaires  ·  Source: AutoFixture/AutoFixture

    [TestFixture]
    public class TestAutoFixture4
    {
        [Test]
        [AutoMoqData]
        public void CreatingAnDummyObjectShouldNotThrow(IFixture fixture)
        {
            fixture.Invoking(x => x.Create<DummyObject>()).ShouldNotThrow();
        }

        [Test]
        [AutoMoqData]
        public void BuildingAnDummyObjectShouldNotThrow(IFixture fixture)
        {
            fixture.Invoking(x => x.Build<DummyObject>().Create()).ShouldNotThrow();
        }
    }

    public class DummyObject
    {
        public Guid Id { get; set; }
        public DummyObject CircularRef { get; set; }
    }

    public class AutoMoqDataAttribute : AutoDataAttribute
    {
        public AutoMoqDataAttribute()
            : base(new Fixture()
                 .Customize(
                    new DummyCustomization()
                ))
        { }
    }

    public class DummyCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customize<DummyObject>(c =>
                c.Without(x => x.CircularRef)
            );
        }
    }

Je viens de mettre à jour AutoFixture de 3.50.6 à 4.0.0.

Le code ci-dessus fonctionne parfaitement sur 3.50.6, mais seul le premier test (en utilisant fixture.Create) réussit en utilisant 4.0.0 (.NET 4.6.2, NUnit3.7.1, FluentAssertions 4.19.4).

Le test utilisant fixture.Build échoue, même si la personnalisation injectée spécifie de ne pas résoudre la référence circulaire.

Ai-je raté quelque chose dans le changelog ou est-ce un bug ?

Merci d'avance :)

question

Commentaire le plus utile

Pensez-vous que l'approche Omitter peut résoudre le problème ? Je ne connaissais pas ce cours et je vais certainement l'essayer de toute façon.

Si vous réécrivez votre personnalisation comme suit, tous les tests commenceront à réussir :

c# public class DummyCustomization : ICustomization { public void Customize(IFixture fixture) { // Don't populate the DummyObject.CircularRef property. fixture.Customizations.Add( new Omitter( new EqualRequestSpecification( typeof(DummyObject).GetProperty(nameof(DummyObject.CircularRef))))); } }

Par conséquent, oui, cela devrait résoudre votre problème 😉 De plus, cette approche ne semble pas trop lourde, elle devrait donc être acceptable.

S'il vous plaît laissez-moi savoir si vous avez d'autres questions sur votre question.

Tous les 5 commentaires

@Dev-I-Ant Merci d'avoir posté le problème ici et pour une description aussi détaillée 👍

Eh bien, ce comportement est le résultat des changements appliqués dans #781. J'ai résolu le problème lorsqu'une personnalisation pouvait affecter d'autres personnalisations. Cela se produit même si vous avez deux personnalisations ultérieures pour le même type - la précédente sera entièrement ignorée.

Lorsque vous utilisez l'API Build<T>() , vous créez en fait une personnalisation temporaire pour le type DummyObject . Cela signifie que toutes les personnalisations précédentes (appliquées via l'API Customize<DummyObject>() ) sont ignorées et ne participent pas. C'est pourquoi vous voyez à nouveau une erreur sur la dépendance circulaire.

Ce type d'isolement est nécessaire, sinon des problèmes très étranges pourraient survenir (voir les problèmes référencés dans le PR mentionné ci-dessus). Par conséquent, pour faire simple, je dirais que le comportement observé n'est pas un bug, mais plutôt une particularité de la conception que nous avons actuellement.


Pour atténuer le problème que vous observez, je vois essentiellement deux façons.

Solution de contournement 1 : ajoutez Omitter pour la propriété

Nous avions déjà une question similaire, vous pouvez donc réutiliser l'approche suggérée ici ou la modifier pour mieux répondre à vos besoins. Évidemment, s'il s'agit d'un scénario courant dans votre projet, vous pouvez créer une méthode d'extension pour accomplir la tâche plus facilement.

Solution de contournement 2 : utilisez plutôt l'API Create<T>()

Dans votre exemple, il n'est pas nécessaire d'utiliser l'API Build<> . Bien sûr, il se peut que vous ayez fourni un exemple simplifié et que vous ayez en fait besoin de cette API. Cependant, j'ai décidé de vous fournir cette option juste au cas où

Faites-moi savoir si ma réponse a aidé à faire la lumière sur ce problème

@zvirja Merci pour vos idées, cela rend les choses plus claires.

J'ai déjà beaucoup lu sur le fait qu'il n'est pas possible d'empiler des personnalisations sur un même type (même si frustrant), et ce changement va certainement me poser quelques soucis, mais je comprends pourquoi vous avez dû changer quelque chose.

Mon cas d'utilisation teste une application utilisant un grand graphe d'objets auquel de nombreuses références circulaires ; le modèle typique (anti-?) où un parent détient une liste d'enfants qui à leur tour font référence à leur parent.

J'aimerais bien changer de modèle mais ce n'est pas possible, du moins pour l'instant.

Donc, créer simplement des objets à l'aide de fixture.Create n'est pas une option et nous les construisons généralement en appelant OmitAllProperties juste après, ce qui limite la puissance de l'AF.

J'avais donc l'habitude de créer des personnalisations qui géraient cela pour moi, en omettant les références circulaires (alors les développeurs n'avaient qu'à construire leurs objets sans avoir à se soucier de savoir s'il y avait des références circulaires ou non, et pouvoir les personnaliser davantage en utilisant With, Sans et ainsi de suite). Mais ce changement casse en quelque sorte tout cela.
Pensez-vous que l'approche Omitter peut résoudre le problème ? Je ne connaissais pas ce cours et je vais certainement l'essayer de toute façon.

Merci encore, et s'il vous plaît laissez-moi savoir si vous pensez que vous pouvez ajouter quelque chose à ma réflexion.

Pensez-vous que l'approche Omitter peut résoudre le problème ? Je ne connaissais pas ce cours et je vais certainement l'essayer de toute façon.

Si vous réécrivez votre personnalisation comme suit, tous les tests commenceront à réussir :

c# public class DummyCustomization : ICustomization { public void Customize(IFixture fixture) { // Don't populate the DummyObject.CircularRef property. fixture.Customizations.Add( new Omitter( new EqualRequestSpecification( typeof(DummyObject).GetProperty(nameof(DummyObject.CircularRef))))); } }

Par conséquent, oui, cela devrait résoudre votre problème 😉 De plus, cette approche ne semble pas trop lourde, elle devrait donc être acceptable.

S'il vous plaît laissez-moi savoir si vous avez d'autres questions sur votre question.

Cela fonctionne parfaitement. Merci beaucoup! Je suppose que tu peux fermer ça alors :)

@Dev-I-Ant Awesome, merci pour le retour ! :)

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

tiesmaster picture tiesmaster  ·  7Commentaires

JoshKeegan picture JoshKeegan  ·  6Commentaires

ecampidoglio picture ecampidoglio  ·  7Commentaires

zvirja picture zvirja  ·  4Commentaires

TroyHouston picture TroyHouston  ·  6Commentaires