C# 使用Autofixture链接自定义项

C# 使用Autofixture链接自定义项,c#,autofixture,C#,Autofixture,我知道,Autofixture在找到能够满足请求的ISpecimenBuilder时停止构建对象。因此,当我应用几个后续定制时,除了最后一个之外,其他所有定制都会被忽略。我该如何组合定制?换句话说,如何修改此代码段: fixture.Customize<SomeClass>(ob => ob.With(x => x.Id, 123)); // this customization is ignored fixture.Customize<SomeClass>(

我知道,
Autofixture
在找到能够满足请求的
ISpecimenBuilder
时停止构建对象。因此,当我应用几个后续定制时,除了最后一个之外,其他所有定制都会被忽略。我该如何组合定制?换句话说,如何修改此代码段:

fixture.Customize<SomeClass>(ob => ob.With(x => x.Id, 123)); // this customization is ignored
fixture.Customize<SomeClass>(ob => ob.With(x => x.Rev, 4341)); // only this one takes place
fixture.Customize<SomeClass>(ob => ob
    .With(x => x.Id, 123)
    .With(x => x.Rev, 4341)); // both customization are applied
fixture.Customize(ob=>ob.With(x=>x.Id,123));//此自定义将被忽略
fixture.Customize(ob=>ob.With(x=>x.Rev,4341));//只有这一次发生了
要与此代码段等效,请执行以下操作:

fixture.Customize<SomeClass>(ob => ob.With(x => x.Id, 123)); // this customization is ignored
fixture.Customize<SomeClass>(ob => ob.With(x => x.Rev, 4341)); // only this one takes place
fixture.Customize<SomeClass>(ob => ob
    .With(x => x.Id, 123)
    .With(x => x.Rev, 4341)); // both customization are applied
fixture.Customize(ob=>ob
.With(x=>x.Id,123)
.带有(x=>x.Rev,4341));//这两种自定义都适用

以下是我的想法:

public class CustomFixture : Fixture
{
    public CustomFixture ()
    {
        this.Inject(this);
    }

    private readonly List<object> _transformations = new List<object>();

    public void DelayedCustomize<T>(Func<ICustomizationComposer<T>, ISpecimenBuilder> composerTransformation)
    {
        this._transformations.Add(composerTransformation);
    }

    public void ApplyCustomize<T>()
    {
        this.Customize<T>(ob =>
        {
            return this._transformations.OfType<Func<ICustomizationComposer<T>, ISpecimenBuilder>>()
                .Aggregate(ob, (current, transformation) => (ICustomizationComposer<T>)transformation(current));
        });
    }

}
公共类CustomFixture:Fixture
{
公共定制设备()
{
这个,注射(这个),;
}
私有只读列表_转换=新列表();
public void DelayedCustomize(Func composer转换)
{
此._transformations.Add(Composer Transformation);
}
public void ApplyCustomize()
{
这个。定制(ob=>
{
返回此值。_.OfType()的转换
.聚合(ob,(当前,转换)=>(ICCustomizationComposer)转换(当前));
});
}
}
以及用法:

var fixture = new CustomFixture();

fixture.DelayedCustomize<SomeClass>(ob => ob.With(x => x.Id, 123)); 
fixture.DelayedCustomize<SomeClass>(ob => ob.With(x => x.Rev, 4341)); 
fixture.ApplyCustomize<SomeClass>();

var obj = fixture.Create<SomeClass>();
// obj.Id == 123
// obj.Rev == 4341
var fixture=new CustomFixture();
fixture.DelayedCustomize(ob=>ob.With(x=>x.Id,123));
fixture.DelayedCustomize(ob=>ob.With(x=>x.Rev,4341));
fixture.ApplyCustomize();
var obj=fixture.Create();
//对象Id==123
//对象版本==4341

由于需要
ApplyCustomize

而不理想。简单的回答是,AutoFixture无法在开箱即用的情况下实现这一点,这是设计的结果。在很多年前的某个时候,我实际上有一个原型,它的行为就像你希望它的行为一样,而不是覆盖以前的定制,但在试用了一段时间后,我得出结论,它太混乱了,当事情变得更复杂时,可能会导致一些难以理解的行为。在我使用AutoFixture的多年中,我也从来没有这样做的必要,所以我对这个用例很好奇。@MarkSeemann这个用例是应用一个类似fluent的界面。因此,我可以使用fixture.WithEmail().WithCache(),而不是fixture.SetupWithEmailAndCache()之类的东西。启用电子邮件和启用缓存都需要在配置对象上设置某些属性以及一些其他操作。但是这些属性和操作对于电子邮件和缓存来说并不相交,所以将这样的设置放在一个函数中是没有意义的。谢谢您的澄清。如果您通常需要在同一测试代码中解决独立的问题,那么这对您的被测系统有什么影响?@MarkSeemann我看不出有任何错误-您如何测试依赖于彼此独立的其他单元的单元?我的示例的错误在于,尽管我声称我的电子邮件和缓存是独立的,但它们确实有一些共同点——这就是配置对象。这违反了单一责任原则,导致了测试问题。另一个例子-我有一个定制,这对于整个测试套件是通用的;只有在一个测试用例中,我想稍微更改设置,但要做到这一点,我需要复制整个定制。