我想按顺序运行我的测试,因为它们会更改同一个数据库,可能会相互影响。我在互联网上尝试了很多解决方案,但都不适用于我。这些解决方案在链接“按顺序执行单元测试(而不是并行执行)”中有描述。
我现在有点困扰。有人对这个问题有什么想法吗?
来自文档:
默认情况下,每个测试类都是唯一的测试集合。
相同测试类中的测试用例不会相互并行运行。
对于来自不同类的测试用例
如果我们需要表明多个测试类不应该互相并行运行,那么我们就将它们放入同一个测试集合中。
这只需要为每个测试类添加一个属性即可,该属性将它们放入同一个具有唯一名称的测试集合中。
[Collection("Database tests")]
public class DeleteTests
{
[Fact]
public void RemovesItemFromDatabase()
{
// test
}
}
[Collection("Database tests")]
public class InsertTests
{
[Fact]
public void InsertsItemToDatabase()
{
// test
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class PriorityAttribute : Attribute
{
public PriorityAttribute(int priority)
{
Priority = priority;
}
public int Priority { get; private set; }
}
public class PriorityOrderer : ITestCaseOrderer
{
public const string Name = "Namespace.PriorityOrderer";
public const string Assembly = "Assembly name";
private static string _priorityAttributeName = typeof(PriorityAttribute).AssemblyQualifiedName;
private static string _priorityArgumentName = nameof(PriorityAttribute.Priority);
private static ConcurrentDictionary<string, int> _defaultPriorities = new ConcurrentDictionary<string, int>();
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) where TTestCase : ITestCase
{
var groupedTestCases = new Dictionary<int, List<ITestCase>>();
var defaultPriorities = new Dictionary<Type, int>();
foreach (var testCase in testCases)
{
var defaultPriority = DefaultPriorityForClass(testCase);
var priority = PriorityForTest(testCase, defaultPriority);
if (!groupedTestCases.ContainsKey(priority))
groupedTestCases[priority] = new List<ITestCase>();
groupedTestCases[priority].Add(testCase);
}
var orderedKeys = groupedTestCases.Keys.OrderBy(k => k);
foreach (var list in orderedKeys.Select(priority => groupedTestCases[priority]))
{
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
foreach (TTestCase testCase in list)
yield return testCase;
}
}
private int PriorityForTest(ITestCase testCase, int defaultPriority)
{
var priorityAttribute = testCase.TestMethod.Method.GetCustomAttributes(_priorityAttributeName).SingleOrDefault();
return priorityAttribute?.GetNamedArgument<int>(_priorityArgumentName) ?? defaultPriority;
}
private int DefaultPriorityForClass(ITestCase testCase)
{
var testClass = testCase.TestMethod.TestClass.Class;
if (!_defaultPriorities.TryGetValue(testClass.Name, out var result))
{
var defaultAttribute = testClass.GetCustomAttributes(_defaultPriorityAttributeName).SingleOrDefault();
result = defaultAttribute?.GetNamedArgument<int>(_priorityArgumentName) ?? int.MaxValue;
_defaultPriorities[testClass.Name] = result;
}
return result;
}
}
为了让测试按顺序运行,请将以下属性添加到需要的类中:
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
public abstract class TestsBase
{
private FieldInfo _counterField;
public TestsBase()
{
_counterField = this.GetType().GetField("_counter", BindingFlags.Static | BindingFlags.NonPublic);
if (GetCounter() == null)
{
var facts = this.GetType().GetMethods()
.Where(m => m.GetCustomAttribute<FactAttribute>() != null);
SetCounter(new bool[facts.Count()]);
}
}
private bool[] GetCounter() => _counterField.GetValue(this) as bool[];
private void SetCounter(bool[] value) => _counterField.SetValue(this, value);
protected void VerifyAndFlip(int testNumber)
{
var counter = GetCounter();
counter.Take(testNumber).Should().AllBeEquivalentTo(true);
counter.Skip(testNumber).Should().AllBeEquivalentTo(false);
counter[testNumber] = true;
SetCounter(counter);
}
}
接下来,使用Priority
属性装饰你的测试方法。
[Fact, Priority(1)]
public async Task Fact1_CorrectValue1_ReturnsTrue()
{
Assert.True(true);
}
[Fact, Priority(2)]
public async Task Fact2_Value2_ReturnsTrue()
{
Assert.True(true);
}