如何添加自定义后应用的常见后处理

joo*_*zek 8 c# autofixture

我为我的模型定义了ISpecimenBuilder并使用它:

new Fixture().Customize(new ModelCustomization());
Run Code Online (Sandbox Code Playgroud)

我想在大多数关于模型的测试中使用它.我还想在我的一个测试类中应用某种形式的后处理.具体来说,我想填补CompanyHistory所有创建的属性Offers.感觉它可以这样做:

fixture.Build<Offer>()
.With(o => o.CompanyHistory, _previouslyCreatedCompanyHistory)
.Create();
Run Code Online (Sandbox Code Playgroud)

Build<T>禁用所有自定义,我需要它们.

我可以这样做吗?

fixture.Build<Offer>()
.WithCustomization(new ModelCustomization()) // there is no such method, but i'd like it to be
.With(o => o.CompanyHistory, _previouslyCreatedCompanyHistory)
.Create();
Run Code Online (Sandbox Code Playgroud)

或者我应该写自己的行为?如果是这样,有人可以为我提供这方面的指导吗?

编辑:我觉得我必须强调我要使用我的常见自定义(ModelCustomization)和后处理器

编辑2:我从一开始就意味着ModelCustomization可以(并且应该)创建Offer和我的待处理后处理器应该使用已创建的样本并填充其某些属性.

Nik*_*nis 5

以下是Postprocessor在这种情况下如何创建和使用a :

[Fact]
public void Test()
{
    var fixture = new Fixture();

    // (You may also include other customizations here.)

    fixture.Customizations.Add(
        new FilteringSpecimenBuilder(
            new Postprocessor(
                new MethodInvoker(
                    new ModestConstructorQuery()),
                new OfferFiller()),
            new OfferSpecification()));

    var offer = fixture.Create<Offer>();
    // -> offer.CompanyHistory has the value supplied in OfferFiller command.
}
Run Code Online (Sandbox Code Playgroud)

OfferFiller命令定义为:

internal class OfferFiller : ISpecimenCommand
{
    public void Execute(object specimen, ISpecimenContext context)
    {
        if (specimen == null)
            throw new ArgumentNullException("specimen");
        if (context == null)
            throw new ArgumentNullException("context");

        var offer = specimen as Offer;
        if (offer == null)
            throw new ArgumentException(
                "The specimen must be an instance of Offer.",
                "specimen");

        Array.ForEach(offer.GetType().GetProperties(), x =>
        {
            if (x.Name == "CompanyHistory ")
                x.SetValue(offer, /*value*/);
            else 
                x.SetValue(offer, context.Resolve(x.PropertyType));
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

OfferSpecification定义为:

internal class OfferSpecification : IRequestSpecification
{
    public bool IsSatisfiedBy(object request)
    {
        var requestType = request as Type;
        if (requestType == null)
            return false;

        return typeof(Offer).IsAssignableFrom(requestType);
    }
}
Run Code Online (Sandbox Code Playgroud)


joo*_*zek 1

我最终写了以下定制:

private class OfferWithCompanyModelCustomization: ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(new FilteringSpecimenBuilder(new Postprocessor(
            new ModelSpecimenBuilder(), new FillModelPropertiesCommand()), new ExactTypeSpecification(typeof(Offer))));
    }

    private class FillModelPropertiesCommand : ISpecimenCommand
    {
        public void Execute(object specimen, ISpecimenContext context)
        {
            var offer = specimen as Offer;
            offer.CompanyHistory = (CompanyHistory)context.Resolve(typeof(CompanyHistory));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这可行,但远非完美。正如您所看到的,我直接引用ModelSpecimenBuilder,所以我依赖于实现(作为后处理器,我不想成为)。

@Nikos 发布的答案并不令人满意,因为他的定制忽略了责任链中以前的定制。

当我们调用 Create 方法时,CompositeSpecimenBuilder 将调用其包含的所有构建器的 Create 方法,直到其中一个构建器提供样本。此时,请求被认为已得到满足,其余构建器将被忽略。

来源:AutoFixture 文档