Каковы принципы декларативного способа настройки приспособления AutoFixture?
Я ранее задавал аналогичный вопрос на что я получил ответ. В то время, ради целесообразности, я механически применил ответ, но теперь я пытаюсь разобраться в том, как механизм декларативной настройки крепления.
Итак, в настоящее время я смотрю на Марк Seemann имеет дело с типами без публичных конструкторов блог сообщение и преобразование его в словах. Это очень похоже на мой оригинальный запрос, но я не могу заставить его работать. Обратите внимание, что данный код на самом деле не является производственным кодом и что это учебное упражнение.
теперь, если это поможет, у меня есть императивный код на GitHub и соответствующий код воспроизводится ниже:
[Fact]
public static void CanOverrideCtorArgs()
{
var fixture = new Fixture();
var knownText = "This text is not anonymous";
fixture.Register<int, IMyInterface>( i => new FakeMyInterface( i, knownText ) );
var sut = fixture.Create<MyClass>();
}
это код, аналогичный приведенному в этом в должности.
следовательно, мой вопрос в том, что я должен знать/читать, чтобы преобразовать этот фрагмент императивного кода будьте декларативны.
2 ответов
Иди читай
- блог Никоса Баксеваниса
- блог Энрико Камидольо
- сообщения автофиксации Марка Seemann (Настройки Инкапсуляции AutoFixture самый лучший)
для больших примеров настроек и как упаковать, смешивать и смешивать их.
главным образом принцип что вы делаете ваши установки как зернистый как вероятный.
затем вам нужно подать их в конвейер обработки либо через:
-
AutoData
- производные атрибуты для глобального материала (т. е. какMyTestConventions
в ответе Марка) - A
CustomizeWith
helper[1] или аналогичный - обман как делать
[Freeze( As ... )]
реализация
автоматизируя это, я бы написал:
[Theory, AutoData]
public static void OutOfBandCustomization(
[CustomizeWith( typeof( MyFakerCustomization ) )] MyClass sut )
{
}
С помощью этого настройка:
public class MyFakerCustomization : ICustomization
{
void ICustomization.Customize( IFixture fixture )
{
var knownText = "This text is not anonymous";
fixture.Register<int, IMyInterface>( i =>
new FakeMyInterface( i, knownText ) );
}
}
очевидно, что регистрация string
и/или с помощью AutoMoqCustomization
также может быть полезным.
Мой генерал-помощников
[1] CustomizeWith
это вспомогательный атрибут (наконечник шляпы к Адам Ясинский):
[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = true )]
sealed class CustomizeWithAttribute : CustomizeAttribute
{
readonly Type _type;
public CustomizeWithAttribute( Type customizationType )
{
if ( customizationType == null )
throw new ArgumentNullException( "customizationType" );
if ( !typeof( ICustomization ).IsAssignableFrom( customizationType ) )
throw new ArgumentException(
"Type needs to implement ICustomization" );
_type = customizationType;
}
public override ICustomization GetCustomization( ParameterInfo parameter )
{
return (ICustomization)Activator.CreateInstance( _type );
}
}
в сторону
один совет: вы можете выразить
fixture.Register<int, IMyInterface>( i =>
new FakeMyInterface( i, knownText ) );
as
fixture.Customize<IMyInterface>(c =>c.FromFactory((int i)=>
new FakeMyInterface(i,knownText)));
тоже. Хотя это не облегчает ваше дело, это более общий способ настройки того, что продолжающийся.
внутри Register
is [В настоящее время]:
fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());
прежде всего, я собираюсь ответить на этот вопрос в предположении, что TypesWithoutPublicCtrs
is определено как в репозитории GitHub OP:
public class TypesWithoutPublicCtrs
{
private readonly IMyInterface _mi;
public TypesWithoutPublicCtrs(IMyInterface mi)
{
_mi = mi;
}
}
причина, по которой я явно вызываю это, заключается в том, что имя является отвлекающим маневром: it тут есть открытый конструктор; у него просто нет по умолчанию конструктор.
в любом случае, AutoFixture легко справляется с отсутствием конструкторов по умолчанию. Проблема не в этом. the TypesWithoutPublicCtrs
сам класс, а скорее IMyInterface
интерфейс. Интерфейсы проблематичны, потому что они вообще не могут быть инициализированы.
таким образом, вам нужно каким-то образом сопоставить интерфейс с конкретным классом. Существуют различные способы сделать это.
одноразовое разрешение
время от времени я использую это одноразовое решение, хотя я считаю, это некрасиво. Тем не менее, это легко и не требует много сложной настройки.
[Theory, AutoData]
public void TestSomething(
[Frozen(As = typeof(IMyInterface))]FakeMyInterface dummy,
TypesWithoutPublicCtrs sut)
{
// use sut here, and ignore dummy
}
этот не особенно приятно, потому что он полагается на побочный эффект , но он работает как автономное одноразовое решение.
однако мне очень нравится делать из этого соглашение, так что то же самое соглашение применяется ко всем тестам в наборе тестов. Тест с использованием такого соглашения может выглядеть так:
[Theory, MyTestConventions]
public void TestSomething(TypesWithoutPublicCtrs sut)
{
// use sut here; it'll automatically have had FakeMyInterface injected
}
на [MyTestConventions]
атрибут может выглядеть так:
public class MyTestConventionsAttribute : AutoDataAttribute
{
public MyTestConventionsAttribute() :
base(new Fixture().Customize(new MyTestConventions())
{}
}
в MyTestConventions
класс должен реализовать интерфейс ICustomization
. Существует несколько способов отображения IMyInterface
to FakeMyInterface
; вот один из них:
public class MyTestConventions : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new TypeRelay(typeof(IMyInterface), typeof(FakeMyInterface)));
}
}
Авто-Издевательский
однако вам может надоесть создавать и поддерживать все эти подделки, поэтому вы также можете превратить AutoFixture в Авто-Издевательство Контейнер. Есть различные варианты для этого, использование Moq, NSubstitute, FakeItEasy и Носорог Издевается.