Как использовать AutoFixture для создания с настраиваемыми свойствами при сохранении настроек типа?

Я пытаюсь использовать автокрепление для создания объекта, но есть определенные свойства, которые я хочу всегда использовать по умолчанию (в то время как остальные могут быть сгенерированы автоматически). Однако всякий раз, когда я настраиваю настройку, она перезаписывается при сборке с настройками.

void Main()
{
    var fixture = new Fixture();
    fixture.Customize<Person>(composer => composer.With(p => p.Name, "Ben"));

    var person = fixture.Build<Person>()
        .With(p => p.DateOfBirth, new DateTime(1900, 1, 1))
        .Create();

    /*  RESULT OF person below
    Name    null
    DateOfBirth 1/1/1900
    StreetAddress   StreetAddressafd6b86b-376a-4355-9a9c-fbae34731453
    State   State019e867b-ac5e-418f-805b-a64146bc06bc
    */
}

public class Person
{
    public string Name { get; set;}

    public DateTime DateOfBirth { get; set;}

    public string StreetAddress { get; set;}

    public string State { get; set;}
}

DateOfBirth свойств Name и DateOfBirth не конфликтуют, поэтому я не знаю, почему Name в итоге становится нулевым. Я ожидаю, что имя будет Ben.

Как я могу получить его, чтобы применить обе настройки (т. DateOfBirth = 1/1/1900 Name = "Ben" и DateOfBirth = 1/1/1900)?

Ответ 1

Как правильно указал @DavidOsborne, поведение, которое вы видите, сконструировано.

Лучший подход - организовать ваши настройки в отдельных классах, а затем включить их по мере необходимости с помощью конкретного тестового сценария.

Объект настройки реализует интерфейс ICustomization и его задачей является настройка объекта Fixture определенным образом. Вот пример:

public class AllPersonsAreNamedBen : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Person>(composer =>
            composer.With(p => p.Name, "Ben"));
    }
}

public class AllPersonsAreBornIn1900 : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Person>(composer =>
            composer.With(p => p.DateOfBirth, new DateTime(1900, 1, 1)));
    }
}

Вы можете включить настройку на конкретном Fixture с помощью метода Customize, например:

fixture.Customize(new AllPersonsAreNamedBen());

или:

fixture.Customize(new AllPersonsAreBornIn1900());

Вы также можете объединить несколько настроек в новый с помощью класса CompositeCustomization:

public class AllPersonsAreNamedBenAndAreBornIn1900 : CompositeCustomization
{
    public AllPersonsAreNamedBenAndAreBornIn1900()
        : base(new AllPersonsAreNamedBen(),
               new AllPersonsAreBornIn1900())
    {
    }
}

в этот момент вы можете просто сказать:

fixture.Customize(new AllPersonsAreNamedBenAndAreBornIn1900());

Однако имейте в виду, что порядок, в котором применяются настройки на Fixture: последний выигрывает и может потенциально переопределять предыдущие, как отметил @MarkSeemann в комментариях. Это тоже по дизайну.

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

public class AllPersonsAreNamedBenAndAreBornIn1900 : CompositeCustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Person>(composer =>
            composer.With(p => p.Name, "Ben")
                    .With(p => p.DateOfBirth, new DateTime(1900, 1, 1)));
    }
}

Как правило, ваши настройки небольшие и сфокусированные позволяют повторно использовать их в разных тестах, объединяя их для конкретных тестовых сценариев.

Ответ 2

Это выглядит по дизайну:

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

... из документации метода Build().

Я ценю, что это, вероятно, не идеальный ответ. Однако в документации дается подсказка о том, как вы можете ее получить.