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

使用AutoNSubstituteClassData时xUnit Theory仅执行一次的原因

问题:AutoNSubstituteClassData搭配xUnit Theory仅执行一次

背景与自定义实现

我使用AutoFixture搭配NSubstitute和xUnit,实现了以下自定义数据属性:

public class AutoNSubstituteDataAttribute : AutoDataAttribute
{
    public AutoNSubstituteDataAttribute()
        : base(() => new Fixture().Customize(new AutoNSubstituteCustomization()))
    {
    }
}

public class AutoNSubstituteClassDataAttribute : CompositeDataAttribute
{
    public AutoNSubstituteClassDataAttribute(Type @class)
        : base(
            new ClassDataAttribute(@class),
            new AutoNSubstituteDataAttribute())
    {
    }
}

问题现象

使用[AutoNSubstituteClassData]时,测试参数注入正常(包括自动模拟的参数),但Theory仅执行一次,无视IEnumerable的元素数量:

// 问题:TestClass1返回2个测试用例,但测试仅执行一次
[Theory]
[AutoNSubstituteClassData(typeof(TestClass1))]
public void AutoNSubstituteClassData_AutoFixture_Injects(string dummy,
    string actualConcrete,
    ITestInterface1 actualMock)
{
    dummy.Should().Be("dummy");
    AssertInjected(actualConcrete, actualMock);
}

public class TestClass1 : IEnumerable<object[]>
{
    public IEnumerator<object[]> GetEnumerator()
    {
        yield return new object[] {"dummy"};
        yield return new object[] {"dummy"};
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

原因分析

你使用CompositeDataAttribute组合ClassDataAttribute和AutoNSubstituteDataAttribute的方式有误:

  • CompositeDataAttribute的默认逻辑是合并多个独立数据源的完整参数集,而非用一个数据源提供部分参数、另一个补充剩余参数。
  • ClassDataAttribute返回的两个参数数组仅包含1个元素,与测试方法需要的3个参数不匹配,会被xUnit直接跳过。
  • AutoNSubstituteDataAttribute返回1个完整的3参数数组,xUnit仅执行这一次测试——你观察到dummy是指定的"dummy"属于巧合,本质上原属性逻辑并未正确组合ClassData的参数和AutoFixture的补充参数。

解决方法

不要使用CompositeDataAttribute,而是自定义继承自ClassDataAttribute的属性,在GetData方法中主动用AutoFixture补充剩余参数:

public class AutoNSubstituteClassDataAttribute : ClassDataAttribute
{
    private readonly IFixture _fixture;

    public AutoNSubstituteClassDataAttribute(Type @class)
        : base(@class)
    {
        _fixture = new Fixture().Customize(new AutoNSubstituteCustomization());
    }

    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        var parameters = testMethod.GetParameters();
        var classData = base.GetData(testMethod).ToList();
        
        foreach (var data in classData)
        {
            var missingParamCount = parameters.Length - data.Length;
            if (missingParamCount <= 0)
            {
                yield return data;
                continue;
            }

            // 生成需要补充的参数
            var additionalParams = new object[missingParamCount];
            for (int i = 0; i < missingParamCount; i++)
            {
                var paramType = parameters[data.Length + i].ParameterType;
                additionalParams[i] = _fixture.Create(paramType);
            }

            // 合并原参数与补充参数
            yield return data.Concat(additionalParams).ToArray();
        }
    }
}

这个实现的核心逻辑:

  1. 先获取ClassDataAttribute提供的所有基础参数集。
  2. 对每个参数集,计算需要AutoFixture补充的参数数量。
  3. 用AutoFixture生成对应类型的补充参数,与原参数合并成完整的参数数组。
  4. 返回所有完整的参数集,确保xUnit能识别到所有测试用例并执行。

验证效果

修改后,TestClass1返回的两个参数集会被分别补充完整,Theory会执行两次,每次的dummy参数都是指定的"dummy",同时actualConcrete和actualMock由AutoFixture自动生成和模拟。

内容的提问来源于stack exchange,提问作者g.pickardou

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:54:58