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

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

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

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());

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

Другие вопросы по тегам