Настройка AutoFixure с использованием FromSeed вызывает исключение

Учитывая два класса:

class Foo
{
    ...
}

class Bar
{
    public Foo FooBar { get; set; }
}

Я установил следующий тест:

void Test()
{
    var fixture = new Fixture();

    fixture.Customize<Foo>(x => x.FromSeed(TestFooFactory));

    var fooWithoutSeed = fixture.Create<Foo>();
    var fooWithSeed = fixture.Create<Foo>(new Foo());

    var bar = fixture.Create<Bar>(); //error occurs here
}

Foo TestFooFactory(Foo seed)
{
    //do something with seed...

    return new Foo();
}

Я могу без проблем создавать объекты Foo напрямую с начальными значениями и без них. Но когда я пытаюсь создать объект Bar со свойством Foo, я получаю ObjectCreationException:

Украшенный ISpecimenBuilder не смог создать экземпляр на основе запроса: Foo. Это может произойти, если запрос представляет интерфейс или абстрактный класс; в этом случае зарегистрируйте ISpecimenBuilder, который может создавать образцы на основе запроса. Если это происходит в строго типизированном выражении сборки, попробуйте указать фабрику с помощью одного из методов IFactoryComposer.

Я ожидаю, что TestFooFactory будет передано начальное значение null во время создания Bar, точно так же, как когда я создал Foo без начального значения. Я что-то не так делаю, или это может быть баг?

В моем реальном сценарии я хочу настроить, как AutoFixture будет использовать заполненные значения для определенных объектов, когда я передаю заданные значения, но я по-прежнему хочу, чтобы AutoFixture по умолчанию вел себя нормально, если не предоставлено начальное значение.


person Nathan A    schedule 10.11.2015    source источник
comment
Кросспостинг на GitHub: github.com/AutoFixture/AutoFixture/issues/467   -  person Mark Seemann    schedule 10.11.2015


Ответы (1)


То, как вы настраиваете Fixture для использования начальных значений , правильно.

Поведение, которое вы видите, является следствием того, как настройка FromSeed изменяет конвейер AutoFixture. Если вам интересно узнать подробности, я описал их здесь. .

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

public class RelaxedSeededFactory<T> : ISpecimenBuilder
{
    private readonly Func<T, T> create;

    public RelaxedSeededFactory(Func<T, T> factory)
    {
        this.create = factory;
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (request != null && request.Equals(typeof(T)))
        {
            return this.create(default(T));
        }

        var seededRequest = request as SeededRequest;

        if (seededRequest == null)
        {
            return new NoSpecimen(request);
        }

        if (!seededRequest.Request.Equals(typeof(T)))
        {
            return new NoSpecimen(request);
        }

        if ((seededRequest.Seed != null)
            && !(seededRequest.Seed is T))
        {
            return new NoSpecimen(request);
        }

        var seed = (T)seededRequest.Seed;

        return this.create(seed);
    }
}

Затем вы можете использовать его для создания объектов типа Foo следующим образом:

fixture.Customize<Foo>(c => c.FromFactory(
    new RelaxedSeededFactory<Foo>(TestFooFactory)));

Эта настройка передаст default(Foo), то есть null, в качестве начального значения в фабричную функцию TestFooFactory при заполнении свойств типа Foo.

person Enrico Campidoglio    schedule 11.11.2015