autofixture&# 39;的声明式方式设置夹具背后的原则是什么?

本文关键字:夹具 设置 背后 原则 是什么 方式 声明 autofixture | 更新日期: 2023-09-27 18:15:14

我以前问过一个类似的问题,我得到了一个答案。当时,为了方便起见,我机械地应用了答案,但现在我正试图掌握声明性地设置fixture的机制是如何的。

所以,我目前正在看Mark 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>();
}

这是一个类似于这篇文章中给出的代码。

因此,我的问题是,为了将这段命令式代码转换为声明性的,我应该知道/读些什么。

autofixture&# 39;的声明式方式设置夹具背后的原则是什么?

阅读

  • Nikos Baxevanis的博客
  • Enrico Camidoglio的博客
  • Mark Seemann的AutoFixture帖子(封装AutoFixture定制是最好的)

查看定制的好例子,以及如何打包、混合和混合它们。

主要原则是使您的自定义尽可能细粒度。

然后,您需要通过以下方式将它们提供给处理管道:
  • AutoData衍生的全局属性(如Mark回答中的MyTestConventions)
  • CustomizeWith辅助器[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是这个helper属性(向Adam Jasinski致敬):

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

 fixture.Customize<IMyInterface>(c =>c.FromFactory((int i)=>
     new FakeMyInterface(i,knownText)));

。虽然这不会使您的情况变得更简单,但这是一种更通用的自定义方式。

内部,Register是[当前]:

fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());

首先,我将在假设TypesWithoutPublicCtrs定义为OP的GitHub存储库的情况下回答这个问题:

public class TypesWithoutPublicCtrs
{
    private readonly IMyInterface _mi;
    public TypesWithoutPublicCtrs(IMyInterface mi)
    {
        _mi = mi;
    }
}
我明确指出这个的原因是它的名字是一个转移注意力的东西:它确实有一个公共构造函数;它只是没有一个默认的构造函数。 无论如何,AutoFixture很容易处理没有默认构造函数的问题。这里的问题不在于TypesWithoutPublicCtrs类本身,而在于IMyInterface接口。接口是有问题的,因为它们根本不能初始化。 因此,您需要以某种方式将接口映射到具体类。有很多方法可以做到这一点。 一次性解决方案

偶尔,我使用这种一次性的解决方案,尽管我发现它很难看。然而,它很容易,不需要很多复杂的设置。
[Theory, AutoData]
public void TestSomething(
    [Frozen(As = typeof(IMyInterface))]FakeMyInterface dummy,
    TypesWithoutPublicCtrs sut)
{
    // use sut here, and ignore dummy
}

这不是特别好,因为它依赖于[Frozen]属性的副作用,但它作为一个自包含的一次性解决方案工作。

然而,我更喜欢对它做一个约定,以便相同的约定适用于测试套件中的所有测试。使用这种约定的测试可能如下所示:

[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映射到FakeMyInterface;这里有一个:

public class MyTestConventions : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(
            new TypeRelay(typeof(IMyInterface), typeof(FakeMyInterface)));
    }
}

Auto-Mocking

然而,你可能会厌倦创建和维护所有这些假的,所以你也可以把AutoFixture变成一个自动模拟容器。有各种各样的选择,利用Moq, NSubstitute, FakeItEasy和Rhino mock。