【问题标题】:How to customize AutoFixture for a default creation of a complex object?如何自定义 AutoFixture 以默认创建复杂对象?
【发布时间】:2021-01-26 14:22:01
【问题描述】:

我有一个相当大的类,它使用实体框架和一些列表属性作为一对多关系。我使用 AutoFixture 来创建测试数据并摆脱循环引用,我设置了很多“无”。但是要一次又一次地复制相同的代码。我不知道如何自定义初始创建。它看起来像这样

作品:

  var entity = fixture.Build<MyObject>()
                .Without(c => c.Cars)
                .Without(c => c.Boats)
                .Without(c => c.Motorcycles)
                .Without(c => c.Skateboards)
                .Create();

我想在每次使用夹具创建 MyObject 时将这些设置为默认值(然后在某些情况下可以覆盖它们)

我尝试创建一个这样的类

public class Conventions: ICustomization
{
    public void Customize(IFixture fixture)
    {

        fixture.Customize<MyObject>
        (c => c
                .Without(c => c.Cars)
                .Without(c => c.Boats)
                .Without(c => c.Motorcycles)
                .Without(c => c.Skateboards)
        );

然后使用它:

var fixture = new Fixture();

fixture.Customize(new Conventions());
var entity = fixture.Build<MyObjec>().Create();

但不,它在递归声明中失败

【问题讨论】:

    标签: c# autofixture


    【解决方案1】:

    您描述的行为是设计的。 .Build&lt;T&gt;() 自定义创建一次性使用的自定义,忽略所有以前分配的自定义。

    如果打算只是使用先前应用的类型自定义创建一个实例,那么您需要做的就是从您的测试中删除 .Build&lt;T&gt;() 语句并直接使用 .Create()。这将避免从头开始创建自定义。

    var fixture = new Fixture();
    
    fixture.Customize(new Conventions());
    var entity = fixture.Create<MyObject>();
    

    由于您使用的是 EntityFramework,您的导航属性很可能被标记为virtual。您可以编写一个IRequestSpecification 来识别虚拟成员并使用Omitter 省略所有导航属性。

    解决您的问题的另一种方法是考虑更改域模型的设计。问问自己,例如,允许域模型的客户“设置/替换”船的集合是否是有效的操作。如果答案是“否”,您可能想要更改模型,以便此规则反映在设计本身中,即使 setter 私有并将集合更改为只读集合。如果您这样做,AutoFixture 将忽略导航属性,您将能够使用适当的业务规则设置它们的值。

    最后,如果您仍想重用自定义的一部分,您可以扩展 AutoFixture 以允许这种操作。 Customize&lt;T&gt;()Build&lt;T&gt;() 都使用 IPostprocessComposer&lt;T&gt; 接口来组成内联自定义。您可以编写一个扩展方法来干预该过程并将自定义委托给第三方。

    public static class ComposerExtensions
    {
        public static IPostprocessComposer<T> Using<T>(this IPostprocessComposer<T> source, IPartialPostprocessComposer<T> composer)
        {
            return composer.Compose(source);
        }
    }
    

    现在假设第三方可能看起来像这样。

    public class MyClassPartialComposer : IPartialPostprocessComposer<MyClass>
    {
        public IPostprocessComposer<MyClass> Compose(IPostprocessComposer<MyClass> source)
        {
            return source
                .Without(x => x.Cars)
                .Without(x => x.Boats);
        }
    }
    

    您的自定义可能如下所示。

    var actual = fixture.Build<MyClass>()
        .Using(new MyClassPartialComposer())
        .With(x => x.Name, "hello")
        .Create();
    

    通过这种方法,您还可以组成部分内联自定义。

    请记住,尽管 AutoFixture 不正式支持最后一种方法,但您必须自己定义扩展方法和部分作曲家实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-03
      • 1970-01-01
      • 2013-09-15
      • 2020-09-12
      • 2021-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多