You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AutoFixture中PostProcessor导致自定义ISpecimenCommand执行两次问题求助

解决AutoFixture自定义输出重复打印的问题

我尝试实现ICustomization,将AutoFixture生成的Fixture值输出到控制台,以辅助诊断夜间构建测试中的间歇性失败。但目前遇到每个值被输出两次的问题,且找到的示例均针对特定类型处理,我需要能处理所有类型的通用实现。以下是LINQPad中的简化代码及重复输出结果:

void Main()
{
  var fixture = new Fixture();
  fixture.Customize(new CustomCustomisation());
  fixture.Create<double>();
}

public class CustomCustomisation : ICustomization
{
  public void Customize(IFixture fixture)
  {
    var engine = ((Fixture)fixture).Engine;
    fixture.Customizations.Add(new Postprocessor(engine, new LogSpecimenCommand()));
  }
}

public class LogSpecimenCommand : ISpecimenCommand
{
  public void Execute(object specimen, ISpecimenContext context)
  {
    if (specimen == null || specimen is NoSpecimen) return;
    Console.WriteLine($"SPECIMEN: {specimen}");
  }
}

输出结果:

SPECIMEN: 213
SPECIMEN: 213

问题原因

重复输出的核心原因是递归调用:你将Postprocessor的构建器设为Fixture的整个Engine,再把这个Postprocessor添加到Customizations集合中。当AutoFixture创建标本时,会先遍历Customizations中的构建器,调用你的Postprocessor,而它又会委托给Engine处理请求;Engine在处理时会再次遍历Customizations,导致Postprocessor被第二次调用,最终触发两次日志输出。


通用解决方案

要实现全类型的日志记录,不需要包裹整个Engine,而是创建一个自定义的ISpecimenBuilder,插入到Customizations的最前端,让所有标本请求都经过它,在获取到标本后统一记录。

修改后的代码

void Main()
{
  var fixture = new Fixture();
  fixture.Customize(new CustomCustomisation());
  fixture.Create<double>();
}

public class CustomCustomisation : ICustomization
{
  public void Customize(IFixture fixture)
  {
    // 将日志构建器插入到Customizations最前面,确保所有请求都经过它
    fixture.Customizations.Insert(0, new LoggingSpecimenBuilder());
  }
}

public class LoggingSpecimenBuilder : ISpecimenBuilder
{
  public object Create(object request, ISpecimenContext context)
  {
    // 委托给后续的构建器创建标本
    var specimen = context.Resolve(request);
    
    // 过滤无效标本并记录有效结果
    if (specimen is not NoSpecimen and not OmitSpecimen and not null)
    {
      Console.WriteLine($"SPECIMEN: [{request}] -> {specimen}");
    }
    
    return specimen;
  }
}

关键说明

  1. LoggingSpecimenBuilder:实现ISpecimenBuilder接口,通过context.Resolve(request)让AutoFixture的默认流程处理标本创建,获取结果后再执行日志记录,彻底避免递归问题。
  2. 插入位置:使用Insert(0, ...)将日志构建器放在Customizations的最前面,确保所有类型的标本请求都会经过它,实现全类型覆盖。
  3. 标本过滤:增加了OmitSpecimen的判断,避免记录AutoFixture内部的跳过标记,让日志更干净。

修改后,每个生成的标本只会被记录一次,且支持所有类型的通用处理。

内容的提问来源于stack exchange,提问作者dp.collins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 20:41:00