Подтвердить что ты не робот

Force AutoFixture использовать самый жадный конструктор

У меня есть тип данных с несколькими конструкторами, и мне нужно AutoFixture выбрать самый жадный (один с большинством параметров). Поведение по умолчанию - это выбрать конструктор с наименьшим числом.

Сообщение в блоге автора, http://blog.ploeh.dk/2009/03/24/HowAutoFixtureCreatesObjects.aspx, похоже, не означает, что есть способ переопределить это поведение, так что это возможно, и если да, то как?

4b9b3361

Ответ 1

Это, безусловно, возможно.

Чтобы изменить стратегию для одного типа (MyClass):

fixture.Customize<MyClass>(c => c.FromFactory(
    new MethodInvoker(
        new GreedyConstructorQuery())));

Чтобы изменить стратегию по всем направлениям:

fixture.Customizations.Add(
    new MethodInvoker(
        new GreedyConstructorQuery()));

Однако, как выясняется, использование GreedyConstructorQuery по всем направлениям, скорее всего, проблематично, как демонстрирует следующий фрагмент кода. Представьте себе класс с этим конструктором:

public Foo(string name)
{
    this.name = name;
}

Этот тест выдает исключение:

[Test]
public void GreedyConstructor()
{
    Fixture fixture = new Fixture();
    fixture.Customizations.Add(new MethodInvoker(new GreedyConstructorQuery()));

    Foo foo = fixture.CreateAnonymous<Foo>();
}

Исключение составляет:

Ploeh.AutoFixture.ObjectCreationException: AutoFixture не смог создать экземпляр из System.SByte *, скорее всего, потому что он не имеет открытого конструктора, является абстрактным или непубличным.

Итак, что это о SByte *? Там нет SByte * в Foo...

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

public String(sbyte* value, int startIndex, int length, Encoding enc);

И там sbyte *...


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

Что вы можете сделать, так это:

Напишите небольшой класс, подобный этому:

public class GreedyEngineParts : DefaultEngineParts
{
    public override IEnumerator<ISpecimenBuilder> GetEnumerator()
    {
        var iter = base.GetEnumerator();
        while (iter.MoveNext())
        {
            if (iter.Current is MethodInvoker)
                yield return new MethodInvoker(
                    new CompositeMethodQuery(
                        new GreedyConstructorQuery(),
                        new FactoryMethodQuery()));
            else
                yield return iter.Current;
        }
    }
}

и создайте экземпляр Fixture следующим образом:

Fixture fixture = new Fixture(new GreedyEngineParts());

Это должно работать.