.NET & Xunit 设置优先级、顺序的单元测试
有时候我们期待以固定的顺序执行测试,比如先新增学生信息,再修改学生信息,再查询、再删除。在这种设计下,如果顺序发生变化,可能导致错误,比如修改一个不存在的学生信息,会导致测试不通过。
这里以Xunit
为例,来看一下如何实现顺序执行单元测试。
直接谷歌xunit Priority unit test
,可以得到一个官方的答案TestCaseOrdering。
定义一个TestPriorityAttribute
,用于获取unit tests的排序,实现ITestCaseOrderer
接口,在OrderTestCases
方法中通过TestPriorityAttribute
进行排序,即可实现顺序执行unit tests。
TestPriorityAttribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TestPriorityAttribute : Attribute
{
public TestPriorityAttribute(int priority)
{
Priority = priority;
}
public int Priority { get; private set; }
}
PriorityOrderer
public class PriorityOrderer : ITestCaseOrderer
{
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) where TTestCase : ITestCase
{
var sortedMethods = new SortedDictionary<int, List<TTestCase>>();
foreach (TTestCase testCase in testCases)
{
int priority = 0;
foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute).AssemblyQualifiedName)))
priority = attr.GetNamedArgument<int>("Priority");
GetOrCreate(sortedMethods, priority).Add(testCase);
}
foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority]))
{
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
foreach (TTestCase testCase in list)
yield return testCase;
}
}
static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key) where TValue : new()
{
TValue result;
if (dictionary.TryGetValue(key, out result)) return result;
result = new TValue();
dictionary[key] = result;
return result;
}
}
这里写一些单元测试方法
[TestCaseOrderer("XUnit.Coverlet.Collector.TestPriority.PriorityOrderer", "XUnit.Coverlet.Collector")]
public class StudentServiceWithPriorityUnitTest
{
static StudentService _studentService;
static Student data = new Student()
{
Name = "test name"
};
static StudentServiceWithPriorityUnitTest()
{
_studentService = new StudentService();
}
[Fact, TestPriority(1)]
public Student Insert_A_Student()
{
_studentService.Insert(data);
data.ShouldNotBeNull();
return data;
}
[Fact, TestPriority(2)]
public void Update_A_Student()
{
data.Name = "update student name";
_studentService.Update(data).ShouldBeTrue();
}
[Fact, TestPriority(3)]
public void Update_A_Student_Failed()
{
var data = new Student()
{
Id = Guid.NewGuid(),
Name = "test name"
};
Should.Throw<DataNotExistException>(() => _studentService.Update(data));
}
[Fact, TestPriority(4)]
public void Get_A_Student()
{
var getData = _studentService.Get(data.Id);
getData.ShouldNotBeNull();
getData.Id.ShouldBeEquivalentTo(data.Id);
}
[Fact, TestPriority(5)]
public void Get_A_Student_Failed()
{
Should.Throw<DataNotExistException>(() => _studentService.Get(Guid.NewGuid()));
}
[Fact, TestPriority(6)]
public void Delete_A_Student()
{
_studentService.Delete(data.Id);
}
[Fact, TestPriority(7)]
public void Delete_A_Student_Failed()
{
Should.Throw<DataNotExistException>(() => _studentService.Delete(Guid.NewGuid()));
}
}
效果如下
调试模式下,也可以看到单元测试是按照顺序执行的。
需要注意的是,根据对单元测试排序,应该避免对单元测试排序
。同时也提供了最佳做法。
最佳做法我们将会在下篇博客中进行讨论。
示例代码
PriorityOrderer
TestPriorityAttribute
StudentServiceWithPriorityUnitTest
参考资料
TestCaseOrdering
使用 dotnet test 和 xUnit 在 .NET Core 中进行 C# 单元测试
对单元测试排序
.NET Core 和 .NET Standard 单元测试最佳做法
学习技术最好的文档就是【官方文档】,没有之一。
还有学习资料【Microsoft Learn】、【CSharp Learn】、【My Note】。
如果,你认为阅读这篇博客让你有些收获,不妨点击一下右下角的【推荐】按钮。
如果,你希望更容易地发现我的新博客,不妨点击一下【关注】。