0
点赞
收藏
分享

微信扫一扫

.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()));
    }
}

效果如下

.NET & Xunit 设置优先级、顺序的单元测试_示例代码


调试模式下,也可以看到单元测试是按照顺序执行的。

需要注意的是,根据对单元测试排序,应该避免对单元测试排序。同时也提供了最佳做法。
最佳做法我们将会在下篇博客中进行讨论。

示例代码

PriorityOrdererTestPriorityAttributeStudentServiceWithPriorityUnitTest

参考资料

TestCaseOrdering对单元测试排序.NET Core 和 .NET Standard 单元测试最佳做法

学习技术最好的文档就是【官方文档】,没有之一。
还有学习资料【Microsoft Learn】、【CSharp Learn】、【My Note】。
推荐】按钮。
如果,你希望更容易地发现我的新博客,不妨点击一下【关注】。



举报

相关推荐

0 条评论