Заставить 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());
Это должно работать.