Autofixture: Создание настроенного экземпляра не удается из-за циклических ссылок

Созданный на 13 янв. 2018  ·  5Комментарии  ·  Источник: 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)
            );
        }
    }

Только что обновил AutoFixture с 3.50.6 до 4.0.0.

Приведенный выше код отлично работает на 3.50.6, но только первый тест (с использованием fixture.Create) проходит с использованием 4.0.0 (.NET 4.6.2, NUnit3.7.1, FluentAssertions 4.19.4).

Тест с использованием fixture.Build завершается неудачно, даже если введенная настройка указывает, что циклическая ссылка не разрешается.

Я что-то пропустил в журнале изменений или это ошибка?

Заранее спасибо :)

question

Самый полезный комментарий

Как вы думаете, подход Omitter может решить проблему? Я не знал этого класса и все равно обязательно попробую.

Если вы перепишете свою настройку следующим образом, все тесты начнут проходить:

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))))); } }

Следовательно, да, это должно решить вашу проблему 😉 Кроме того, этот подход не выглядит слишком громоздким, поэтому должен быть приемлемым.

Пожалуйста, дайте мне знать, если у вас возникнут дополнительные вопросы по вашему вопросу.

Все 5 Комментарий

@ Dev-I-Ant Спасибо, что разместили здесь проблему и за такое подробное описание 👍

Что ж, такое поведение является результатом изменений, внесенных в # 781. Я исправил проблему, когда одна настройка могла повлиять на другие настройки. Это происходит, даже если у вас есть две последующие настройки для одного и того же типа - предыдущая будет полностью проигнорирована.

Когда вы используете Build<T>() API, вы фактически создаете временную настройку для типа DummyObject . Это означает, что все предыдущие настройки (примененные через Customize<DummyObject>() API) игнорируются и не участвуют. Вот почему вы снова видите ошибку о круговой зависимости.

Такая изоляция необходима, иначе могут возникнуть очень странные проблемы (см. Упомянутые проблемы в упомянутом выше PR). Поэтому для простоты я бы сказал, что наблюдаемое поведение не является ошибкой, а скорее особенностью дизайна, который у нас есть в настоящее время.


Чтобы смягчить проблему, которую вы наблюдаете, я вижу в основном два способа.

Обходной путь 1. Добавьте Omitter для свойства

У нас уже был аналогичный вопрос, поэтому вы можете повторно использовать предложенный здесь подход или изменить его, чтобы он лучше соответствовал вашим потребностям. Очевидно, что если это распространенный сценарий в вашем проекте, вы могли бы создать метод расширения для более легкого решения задачи.

Обходной путь 2. Вместо этого используйте Create<T>() API

В вашем примере нет необходимости использовать Build<> API. Конечно, возможно, вы предоставили упрощенный образец и вам действительно нужен этот API. Однако я решил предоставить вам этот вариант на всякий случай 😃

Дайте мне знать, помог ли мой ответ пролить свет на этот вопрос 😉

@zvirja Спасибо за понимание, это проясняет ситуацию.

Я уже много читал о том, что наложение настроек на один и тот же тип невозможно (даже если это расстраивает), и это изменение, безусловно, доставит мне некоторые проблемы, но я понимаю, почему вам пришлось что-то менять.

Мой вариант использования - тестирование приложения с использованием большого графа объектов, на который много циклических ссылок; типичный (анти -?) шаблон, в котором родитель хранит список дочерних элементов, которые, в свою очередь, ссылаются на своего родителя.

Я бы хотел изменить модель, но это невозможно, по крайней мере, на данный момент.

Поэтому простое создание объектов с помощью fixture.Create не вариант, и мы обычно создаем их, вызывая OmitAllProperties сразу после этого, что ограничивает возможности AF.

Поэтому я использовал некоторые настройки, которые справлялись с этим для меня, опуская циклические ссылки (тогда разработчикам просто нужно было создавать свои объекты, не заботясь о том, есть ли циклические ссылки или нет, и имея возможность дополнительно настраивать их с помощью With, Без и тд). Но это изменение как бы ломает все это.
Как вы думаете, подход Omitter может решить проблему? Я не знал этого класса и все равно обязательно попробую.

Еще раз спасибо, и, пожалуйста, дайте мне знать, если вы думаете, что можете добавить что-то к моему отражению.

Как вы думаете, подход Omitter может решить проблему? Я не знал этого класса и все равно обязательно попробую.

Если вы перепишете свою настройку следующим образом, все тесты начнут проходить:

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))))); } }

Следовательно, да, это должно решить вашу проблему 😉 Кроме того, этот подход не выглядит слишком громоздким, поэтому должен быть приемлемым.

Пожалуйста, дайте мне знать, если у вас возникнут дополнительные вопросы по вашему вопросу.

Это прекрасно работает. Большое спасибо! Думаю, тогда вы можете закрыть это :)

@ Dev-I-Ant Awesome, спасибо за отзыв! :)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги