[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 завершается неудачно, даже если введенная настройка указывает, что циклическая ссылка не разрешается.
Я что-то пропустил в журнале изменений или это ошибка?
Заранее спасибо :)
@ Dev-I-Ant Спасибо, что разместили здесь проблему и за такое подробное описание 👍
Что ж, такое поведение является результатом изменений, внесенных в # 781. Я исправил проблему, когда одна настройка могла повлиять на другие настройки. Это происходит, даже если у вас есть две последующие настройки для одного и того же типа - предыдущая будет полностью проигнорирована.
Когда вы используете Build<T>()
API, вы фактически создаете временную настройку для типа DummyObject
. Это означает, что все предыдущие настройки (примененные через Customize<DummyObject>()
API) игнорируются и не участвуют. Вот почему вы снова видите ошибку о круговой зависимости.
Такая изоляция необходима, иначе могут возникнуть очень странные проблемы (см. Упомянутые проблемы в упомянутом выше PR). Поэтому для простоты я бы сказал, что наблюдаемое поведение не является ошибкой, а скорее особенностью дизайна, который у нас есть в настоящее время.
Чтобы смягчить проблему, которую вы наблюдаете, я вижу в основном два способа.
Omitter
для свойстваУ нас уже был аналогичный вопрос, поэтому вы можете повторно использовать предложенный здесь подход или изменить его, чтобы он лучше соответствовал вашим потребностям. Очевидно, что если это распространенный сценарий в вашем проекте, вы могли бы создать метод расширения для более легкого решения задачи.
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, спасибо за отзыв! :)
Самый полезный комментарий
Если вы перепишете свою настройку следующим образом, все тесты начнут проходить:
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))))); } }
Следовательно, да, это должно решить вашу проблему 😉 Кроме того, этот подход не выглядит слишком громоздким, поэтому должен быть приемлемым.
Пожалуйста, дайте мне знать, если у вас возникнут дополнительные вопросы по вашему вопросу.