目录
- 前言
- 正文
- 参考资料
前言
在理想情况下,单元测试的运行顺序不重要,最佳做法是避免对单元测试排序。但有时,你可能希望按照特定顺序运行单元测试,本文将演示如何通过自定义特性的方式对测试进行排序。
正文
首先定义一个要依赖的属性:
/// <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 : structwhere 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);}
}
改变 Test1
、Test2
、测试方法的 TestPriority
特性的值,通过断点调试测试类,发现测试类的执行顺序每次都按照 Priority 从小到大的顺序执行。
注意:
测试类要添加TestCaseOrdererAttribute
特性标注,特性参数的含义如下:
ordererTypeName
:实现ITestCaseOrderer
接口的类的完整名称。ordererAssemblyName
:ordererTypeName
对应类所在的程序集名称。
参考资料
微软官网:对单元测试排序