Autofixture: Не удается применить настройки из базового класса или интерфейса в 4.0.0-rc1 (регрессия)

Созданный на 20 нояб. 2017  ·  10Комментарии  ·  Источник: AutoFixture/AutoFixture

Следующие тесты проходят успешно в AutoFixture 3.51.0 но не проходят в 4.0.0-rc1 :
(даже в последней фиксации в мастере: c856cd6 )

`` С #
[Факт]
public void ShouldApplyCustomizationsFromBaseClass ()
{
var fixture = new Fixture ();
приспособление.(c => c. Без (bc => bc.Id));
var res = fixture.Create();
Assert.Equal (0, res.Id);
}

[Факт]
public void ShouldApplyCustomizationsFromInterface ()
{
var fixture = new Fixture ();
приспособление.(c => c. Без (bc => bc.Id));
var res = fixture.Create();
Assert.Equal (0, res.Id);
}

публичный интерфейс IInterface
{
int Id {получить; установленный; }
}

открытый класс BaseClass
{
public int Id {получить; установленный; }
}

открытый класс ImplClass: BaseClass, IInterface
{

}
`` ''

question

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

Спасибо, что подняли вопрос. Что ж, это желаемое изменение, и оно было применено специально.

Ранее у нас была проблема, когда настройка одного подтипа могла повлиять на другой подтип. Например:

`` С #
class Base {общедоступная строка Common {получить; установленный; }}
class Child1: Base {}
class Child2: Base {}

[Факт]
public void EnsureCustomizationAreNotAffected ()
{
var fixture = new Fixture ();
приспособление.(c => c.With (x => x.Common, «пустышка»));

var result = fixture.Create<Child2>();

Assert.NotNull(result.Common);

}


In the v3 this test failed, causing a lot of confusion to people. As you might imagine, the scenarios were more complicated and it was very non-obvious why the particular members are not initialized. 

For instance, the following code will not work, which again proves that API is not designed for that.
```c#
fixture.Customize<Base>(c => c.With(x => x.Common, "foo"));

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

Если вы все же хотите опустить базовые свойства / свойства интерфейса, вы можете использовать следующий фрагмент:
`` С #
частный класс SamePropertySpecification: IRequestSpecification
{
закрытый только для чтения Тип _declaringType;
частная строка только для чтения _name;

public SamePropertySpecification(Type declaringType, string name)
{
    _declaringType = declaringType;
    _name = name;
}

public bool IsSatisfiedBy(object request)
{
    if (request is PropertyInfo pi)
    {
        return pi.DeclaringType == this._declaringType &&
               pi.Name.Equals(this._name, StringComparison.Ordinal);
    }

    return false;
}

}

[Факт]
public void TestBasePropertyOmitting ()
{
var fixture = new Fixture ();

fixture.Customizations.Add(new Omitter(new SamePropertySpecification(typeof(Base), nameof(Base.Common))));

var result = fixture.Create<Child1>();
Assert.Null(result.Common);

}
`` ''

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

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

Кстати: это то, что вы хотите добавить в список критических изменений? Может помешать задаче того же вопроса.

Невозможно отказаться от старого поведения для одной настройки всех настроек, верно?

Это то, что вы хотите добавить в список критических изменений?

Ну, это упомянуто в разделе исправлений ошибок . На самом деле это не критическое изменение, поскольку предыдущее поведение никогда не определялось как «особенность». Скорее, это был нежелательный побочный эффект, и он был окончательно устранен 😉

Невозможно отказаться от старого поведения для одной настройки всех настроек, верно?

Нет, я считаю, что такого пути нет, потому что я полностью переработал подход.

Пожалуйста, протестируйте фрагмент и, если он работает - не стесняйтесь закрыть проблему. Или дайте мне знать, если у вас возникнут дополнительные вопросы 😃

Я настроил его так, чтобы он также работал для интерфейсов, и сделал метод расширения, чтобы его можно было использовать в Fixture.Customize(...) '. Кажется немного более хакерским, чем хотелось бы, тем более, что способ, которым он работал в v3, был идеальным для нас. Но поскольку наш единственный вариант использования - это пропуск свойств, и он (что наиболее важно) работает, это поможет. Спасибо!

@nphmuller Извините, что я нарушил ваш аккуратный и аккуратный код, и теперь вам нужно выжить 😄 Но я верю, что ваша жертва была принесена на благо всех нас, поэтому вы не будете слишком винить нас 😅

Вообще никакой вины. Прекрасно понимаю ваши рассуждения и даже соглашусь. Просто отстой для меня 😉

Возможно, позже погрузимся в проблему и увидим более простой способ. Пока все в порядке.

Просто из любопытства - каков ваш реальный вариант использования? Потому что я никогда не встречал потребности в такой функциональности ... Обычно, когда я пишу тест, мне нужно настроить только определенный тип, а не настраивать базовый или интерфейсный тип. Пишете какую-то глобальную кастомизацию?

В этом случае мы используем AutoFixture в качестве генератора тестовых данных для интеграционных тестов Entity Framework.

У нас есть несколько базовых типов или интерфейсов, которые используют наши объекты и которые содержат общие свойства. Id, tenant-id (и свойство навигации) и тому подобное.

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

Так что проще просто исключить эти свойства и при необходимости подписаться. На уровне базового класса, потому что в противном случае нужно было бы написать одно и то же правило исключения для каждого типа сущности.

@nphmuller Спасибо за разъяснения, теперь все ясно 😉 Да, похоже, сценарий имеет смысл, но я должен признать, что это похоже на то, что вам нужно довольно редко.

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

Смежные вопросы

ploeh picture ploeh  ·  3Комментарии

ploeh picture ploeh  ·  7Комментарии

Ephasme picture Ephasme  ·  3Комментарии

joelleortiz picture joelleortiz  ·  4Комментарии

mjfreelancing picture mjfreelancing  ·  4Комментарии