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

如何让并行xUnit测试的每个测试类获取唯一测试数据?

问题

我有一个包含单个测试方法的xUnit测试类,原本用于加载包含测试描述的文件,现在希望将这些测试用例并行执行。

由于xUnit测试类的测试方法始终串行运行,我通过定义子类继承测试方法的方式实现了并行执行,这部分生效了。

但问题在于每个测试类都会独立加载测试数据,目前我尝试通过线程安全的方式让每个测试方法获取唯一的测试项,编写了如下代码:

using System.Collections.Concurrent;
using Xunit.Abstractions;
[assembly: CollectionBehavior(MaxParallelThreads = 10)]
namespace MultiTest;
public class Data
{
    public static readonly ConcurrentStack<string[]> stack = new ConcurrentStack<string[]>();
    static Data()
    {
        List<string[]> strings = new List<string[]>() {
            new string[] { "one" },
            new string[] { "two" },
            new string[] { "three" },
            new string[] { "four" },
            new string[] { "five" },
            new string[] { "six" },
            new string[] { "seven" },
            new string[] { "eight" },
            new string[] { "nine" },
            new string[] { "ten" }
        };
        foreach (var str in strings)
            stack.Push(str);
    }
    public static IEnumerable<object[]> GetTestData()
    {
        foreach (var str in stack)
            yield return new object[] { str };
    }
}
public abstract class UnitTest1
{
    private readonly ITestOutputHelper _testOutputHelper;
    public UnitTest1(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }
    public static IEnumerable<object[]> GetTestData() => Data.GetTestData();
    [Theory]
    [MemberData(nameof(GetTestData))]
    public void Test1(string arg)
    {
        _testOutputHelper.WriteLine($"Start: {arg}");
        Thread.Sleep(1000);
        _testOutputHelper.WriteLine($"Done");
    }
}
public class TestA : UnitTest1
{
    public TestA(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
    {
    }
}
public class TestB : UnitTest1
{
    public TestB(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
    {
    }
}
public class TestC : UnitTest1
{
    public TestC(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
    {
    }
}
public class TestD : UnitTest1
{
    public TestD(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
    {
    }
}

当前代码运行后,10个测试用例每个都会被执行4次,因为每个测试类都会从迭代器获取相同的全部数据。如何确保在并行环境中,每个测试类实例的测试方法都能从测试数据源获取唯一的测试项?

解决方案

核心问题在于GetTestData方法只是遍历整个ConcurrentStack,没有实现取走并移除元素的逻辑,导致每个测试类都会拿到全部10条数据。要实现每个测试用例获取唯一测试项,需要修改数据获取逻辑,结合线程安全的弹出操作:

修改思路

  1. 替换数据获取逻辑:用ConcurrentStack.TryPop方法从栈中取出元素,一旦取出就从栈中移除,确保每个测试项只会被获取一次。
  2. 简化数据存储:去掉不必要的字符串数组包装,直接存储字符串,减少冗余。
  3. 调整迭代器逻辑:通过循环尝试弹出元素,直到栈为空,让xUnit自动遍历所有可用的唯一测试项。

修改后的完整代码

using System.Collections.Concurrent;
using Xunit.Abstractions;
[assembly: CollectionBehavior(MaxParallelThreads = 10)]
namespace MultiTest;

public class Data
{
    private static readonly ConcurrentStack<string> _testItems = new ConcurrentStack<string>();
    
    static Data()
    {
        var items = new List<string> { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
        foreach (var item in items)
            _testItems.Push(item);
    }

    public static IEnumerable<object[]> GetUniqueTestData()
    {
        while (_testItems.TryPop(out var item))
        {
            yield return new object[] { item };
        }
    }
}

public abstract class UnitTest1
{
    protected readonly ITestOutputHelper _testOutputHelper;

    public UnitTest1(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }

    public static IEnumerable<object[]> GetTestData() => Data.GetUniqueTestData();

    [Theory]
    [MemberData(nameof(GetTestData))]
    public void Test1(string arg)
    {
        _testOutputHelper.WriteLine($"Start: {arg}");
        Thread.Sleep(1000);
        _testOutputHelper.WriteLine($"Done: {arg}");
    }
}

public class TestA : UnitTest1 { public TestA(ITestOutputHelper helper) : base(helper) { } }
public class TestB : UnitTest1 { public TestB(ITestOutputHelper helper) : base(helper) { } }
public class TestC : UnitTest1 { public TestC(ITestOutputHelper helper) : base(helper) { } }
public class TestD : UnitTest1 { public TestD(ITestOutputHelper helper) : base(helper) { } }

关键说明

  • 线程安全的弹出操作:ConcurrentStack.TryPop是原子操作,确保多个并行测试类不会拿到同一个测试项,弹出后元素从栈中永久移除,避免重复执行。
  • 动态测试用例生成:迭代器通过while循环动态获取剩余的测试项,xUnit会自动识别所有可用的测试用例,无需手动指定数量。
  • 并行执行分配:4个测试类会并行从栈中获取不同的测试项,最终10个测试用例各执行一次,不会重复。

内容的提问来源于Stack Exchange,提问作者Matt W

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:42:47