Установка значения свойства для дочернего экземпляра на фиксированное значение с помощью Autofixture

Можно ли назначить фиксированное значение для свойства дочернего экземпляра при создании родителя с помощью Autofixture? Он добавит значения по умолчанию ко всем свойствам дочернего экземпляра, как шарм, но я хотел бы переопределить и присвоить определенное значение одному из свойств дочернего экземпляра.

Учитывая это отношение родитель/ребенок:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public int Number { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }
}

Я хотел бы присвоить определенную ценность свойству City в экземпляре адреса. Я думал о строках этого тестового кода:

var fixture = new Fixture();

var expectedCity = "foo";

var person = fixture
    .Build<Person>()
    .With(x => x.Address.City, expectedCity)
    .Create();

Assert.AreEqual(expectedCity, person.Address.City);

Это невозможно. Я думаю, по исключению отражения

System.Reflection.TargetException : Object does not match target type.

... что Autofixture пытается присвоить значение атрибуту City в экземпляре Person вместо экземпляра Address.

Любые предложения?

И да, я знаю, что я мог бы просто добавить дополнительный шаг, как показано ниже:

var fixture = new Fixture();

var expectedCity = "foo";

// extra step begin
var address = fixture
    .Build<Address>()
    .With(x => x.City, expectedCity)
    .Create();
// extra step end

var person = fixture
    .Build<Person>()
    .With(x => x.Address, address)
    .Create();

Assert.AreEqual(expectedCity, person.Address.City);

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

Примечание. Я использую Autofixture v3.22.0

Ответы

Ответ 1

Не следует игнорировать вопрос, но самым простым решением могло бы быть следующее:

[Fact]
public void SimplestThingThatCouldPossiblyWork()
{
    var fixture = new Fixture();
    var expectedCity = "foo";
    var person = fixture.Create<Person>();
    person.Address.City = expectedCity;
    Assert.Equal(expectedCity, person.Address.City);
}

Назначение явных значений свойствам - это то, что большинство языков уже превосходит (возможно, С#), поэтому я не думаю, что AutoFixture нуждается в сложном DSL для воспроизведения половины этой функции.

Ответ 2

Для полноты, здесь другой способ сделать это:

fixture.Customize<Address>(c => 
    c.With(addr => addr.City, "foo"));

var person = fixture.Create<Person>();

Это позволит настроить создание всех экземпляров Address

Если вы в конечном итоге используете это достаточно часто, может быть полезно обернуть его внутри ICustomization:

public class AddressConventions : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Address>(c => 
            c.With(addr => addr.City, "foo"));
    }
}

fixture.Customize(new AddressConventions());

Ответ 3

Вы можете использовать метод Do в построителе:

var person = this.fixture
               .Build<Person>()
               .Do( x => x.Address.City = expectedCity )
               .Create();

Вы также можете ввести и заморозить свой экземпляр на прибор:

this.fixture.Inject( expectedCity );