cmliu
1,演示环境:windows 10企业版+Visual Studio 2019;.NET Core3.1;xUnit 2.4.1;.NET Standard 2.0.3
3,场景描述:前几年在写单元测试时,经常要实现从数据库增删改查的流水线式的单元测试代码;每次修改业务逻辑或者代码后,只要一次运行,就可以把核心功能逻辑调试完;这个过程中会新增数据,还要在结束时把新增的数据删除
这中间就涉及到了顺序测试的;
解决这个问题有两种方式:1,自己写控制执行顺序的执行方法;2,使用xUnit的顺序执行接口;3,自己写jmeter脚本
刚好最近又有个东西要写测试;于是就把.NET Core下使用xUnit顺序执行的这方面的小东西做了个小总结
场景是这样的,调用目标微服务的暴露的某个模块的接口;需要按顺序执行创建表,查询表,修改表,删除表接口;
本文只粘贴了核心的代码,详细demo代码地址可以前往github下载:https://github.com/SaorenXi/XunitTestDemo.git
4,思路:在xUnit的xunit.core程序集中的Xunit.Sdk命名空间下;有一个用于控制执行顺序的接口:ITestCaseOrderer
如下代码为原程序集定义:
#region 程序集 xunit.core, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c
// C:\Users\Administrator\.nuget\packages\xunit.extensibility.core\2.4.1\lib\netstandard1.1\xunit.core.dll
#endregion using System.Collections.Generic;
using Xunit.Abstractions; namespace Xunit.Sdk
{
//
// 摘要:
// A class implements this interface to participate in ordering tests for the test
// runner. Test case orderers are applied using the Xunit.TestCaseOrdererAttribute,
// which can be applied at the assembly, test collection, and test class level.
public interface ITestCaseOrderer
{
//
// 摘要:
// Orders test cases for execution.
//
// 参数:
// testCases:
// The test cases to be ordered.
//
// 返回结果:
// The test cases in the order to be run.
IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) where TTestCase : ITestCase;
}
}
5,为了做方法顺序控制;定义一个名为OrderAttribute属性标签,标签上有个名为Sort的int型属性,用于标记方法的顺序;如下定义(示例是定义在:XUnitTest.Orders命名空间下)
using System; namespace XUnitTest.Orders
{
/// <summary>
/// 测试方法的执行顺序
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class OrderAttribute : Attribute
{
/// <summary>
/// 顺序
/// </summary>
public int Sort { get; set; }
public OrderAttribute(int sort)
{
this.Sort = sort;
}
}
}
6,实现ITestCaseOrderer接口(//按照Order标签上的Sort属性,从小到大的顺序执行),如下定义(示例定义在:SchemaTest命名空间下)
using System.Collections.Generic;
using System.Linq;
using Xunit.Abstractions;
using Xunit.Sdk;
using XUnitTest.Orders; namespace SchemaTest
{
/// <summary>
/// 单元测试的排序策略
/// </summary>
public class TestOrders : ITestCaseOrderer
{
/// <summary>
/// 执行顺序
/// </summary>
/// <typeparam name="TTestCase"></typeparam>
/// <param name="testCases"></param>
/// <returns></returns>
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) where TTestCase : ITestCase
{
string typeName = typeof(OrderAttribute).AssemblyQualifiedName; ;
var result = testCases.ToList();
result.Sort((x, y) =>
{
var xOrder = x.TestMethod.Method.GetCustomAttributes(typeName)?.FirstOrDefault();
if (xOrder == null)
{
return 0;
}
var yOrder = y.TestMethod.Method.GetCustomAttributes(typeName)?.FirstOrDefault();
if (yOrder == null)
{
return 0;
}
var sortX = xOrder.GetNamedArgument<int>("Sort");
var sortY = yOrder.GetNamedArgument<int>("Sort");
//按照Order标签上的Sort属性,从小到大的顺序执行
return sortX - sortY;
});
return result;
}
}
}
7,使用;在目标的单元测试类上打上标签使用顺序执行实现类的标签;如下定义(示例中的目标类为:SchemaTest命名空间下的SchemaApiTest类)
//省略其他代码
using Xunit;
using XUnitTest.Orders; namespace SchemaTest
{
//[TestCaseOrderer("ITestCaseOrderer的实现类名称", "ITestCaseOrderer的实现类所在的程序集名称")]
[TestCaseOrderer("SchemaTest.TestOrders", "SchemaTest")]
public class SchemaApiTest
{
//省略其他代码
}
}
8,在目标的测试方法上打上Order标签,定义执行顺序,如下定义(示例使用的是mock数据和接口)
using Xunit;
using XUnitTest.Orders; namespace SchemaTest
{
[TestCaseOrderer("SchemaTest.TestOrders", "SchemaTest")]
public class SchemaApiTest
{
private string _testSchemaCode = $"xunittest123";
private static SchemaDTO _schemaDto = null; /// <summary>
/// 创建记录
/// </summary>
[Fact, Order(1)]
public void AddSchema()
{
var response = new MockClient().AddSchema(new SchemaDTO
{
SchemaCode = _testSchemaCode,
Name = "哈哈"
});
Assert.True(response);
} /// <summary>
/// 查询刚刚创建的记录
/// </summary>
[Fact, Order(2)]
public void GetSchema()
{
var response = new MockClient().GetSchema(_testSchemaCode);
//临时记录
_schemaDto = response;
Assert.NotNull(response);
Assert.Equal(_testSchemaCode, response.SchemaCode);
Assert.Equal("哈哈", response.Name);
} /// <summary>
/// 更新刚刚创建的数据记录
/// </summary>
[Fact, Order(3)]
public void UpdateSchema()
{
_schemaDto.Name = "jiujiu";
var response = new MockClient().UpdateSchema(_schemaDto);
Assert.True(response);
} /// <summary>
/// 查询更新后的记录
/// </summary>
[Fact, Order(4)]
public void GetAfterUpdatedSchema()
{
var response = new MockClient().GetSchema(_testSchemaCode);
Assert.NotNull(response);
Assert.Equal(_testSchemaCode, response.SchemaCode);
Assert.Equal("jiujiu", response.Name);
} /// <summary>
/// 删除记录
/// </summary>
[Fact, Order(5)]
public void RemoveSchema()
{
var response = new MockClient().RemoveSchema(_testSchemaCode);
Assert.True(response);
}
}
}
9,测试