Loading

自定义 xUnit 单元测试顺序

前言

在理想情况下,单元测试的运行顺序不重要,最佳做法是避免对单元测试排序。但有时,你可能希望按照特定顺序运行单元测试,本文将演示如何通过自定义特性的方式对测试进行排序。

正文

首先定义一个要依赖的属性:

/// <summary>
/// 测试优先级特性
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TestPriorityAttribute(int priority) : Attribute
{
    /// <summary>
    /// 优先级
    /// </summary>
    public int Priority { get; private set; } = priority;
}

然后实现 ITestCaseOrderer 接口:

public class PriorityOrderer : ITestCaseOrderer
{
    private static readonly string assemblyName = typeof(TestPriorityAttribute).AssemblyQualifiedName!;
    private static readonly string argumentName = nameof(TestPriorityAttribute.Priority);

    public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases)
        where TTestCase : ITestCase
    {
        var sortedMethods = new SortedDictionary<int, List<TTestCase>>();

        IAttributeInfo? attributeInfo;
        foreach (TTestCase testCase in testCases)
        {
            attributeInfo = testCase.TestMethod.Method.GetCustomAttributes(assemblyName).FirstOrDefault();
            if (attributeInfo is null)
            {
                GetOrCreate(sortedMethods, 0).Add(testCase);
                continue;
            }

            GetOrCreate(sortedMethods, attributeInfo.GetNamedArgument<int>(argumentName)).Add(testCase);
        }

        foreach (TTestCase testCase in sortedMethods.Keys.SelectMany(priority =>
            sortedMethods[priority].OrderBy(internalTtestCase => internalTtestCase.TestMethod.Method.Name)))
        {
            yield return testCase;
        }
    }

    private static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key)
        where TKey : struct
        where TValue : new()
    {
        if (dictionary.TryGetValue(key, out TValue? result))
        {
            return result;
        }

        return dictionary[key] = new TValue();
    }
}

最后在测试类中使用 TestPriorityAttribute 设置顺序:

[TestCaseOrderer(ordererTypeName: "Blog.Core.WebApi.Test.PriorityOrderer", ordererAssemblyName: "Blog.Core.WebApi.Test")]
public class XUnitInitDemo
{
    [Fact, TestPriority(1)]
    public void Test1()
    {
        Assert.Equal(1, 1);
    }

    [Fact, TestPriority(-2)]
    public void Test2()
    {
        Assert.Equal(1, 1);
    }
}

改变 Test1Test2、测试方法的 TestPriority 特性的值,通过断点调试测试类,发现测试类的执行顺序每次都按照 Priority 从小到大的顺序执行。

注意:
测试类要添加TestCaseOrdererAttribute特性标注,特性参数的含义如下:

  • ordererTypeName:实现 ITestCaseOrderer 接口的类的完整名称
  • ordererAssemblyName: ordererTypeName 对应类所在的程序集名称。

参考资料

微软官网:对单元测试排序

posted @ 2025-08-14 21:58  Qanx  阅读(35)  评论(0)    收藏  举报