Каковы принципы декларативного способа настройки приспособления 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 ответов


Иди читай

для больших примеров настроек и как упаковать, смешивать и смешивать их.

главным образом принцип что вы делаете ваши установки как зернистый как вероятный.

затем вам нужно подать их в конвейер обработки либо через:

  • 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 и Носорог Издевается.