在MSTest中,如何指定某些测试方法不能运行并行?

In MSTest, how can I specify that certain test methods cannot be run in parallel with each other?

我有一大组测试网站服务器的集成测试。大多数这些测试都可以并行 运行。但是,我有一些更改设置并且在 运行 并行时可能导致彼此失败。

作为一个简化的例子,假设我进行了这些测试:

TestPrice_5PercentTax
TestPrice_10PercentTax
TestPrice_NoTax
TestInventory_Add10Items
TestInventory_Remove10Items

库存测试互不干扰,不受价格测试影响。但是价格测试将更改 Tax 设置,因此如果 510 运行 并行,10 最终可能会在 5 已完成,而 5 会失败,因为它看到 10% 的税而不是预期的 5%。

我想为三个价格测试定义一个类别,并说它们不能同时运行。他们可以 运行 与任何其他测试同时进行,但不能与其他价格测试同时进行。有没有办法在 MSTest 中执行此操作?

MsTest v2 具有以下功能

[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]
// Notice the assembly bracket, this can be compatible or incompatible with how your code is built

namespace UnitTestProject1
{
    [TestClass]
    public class TestClass1
    {
        [TestMethod]
        [DoNotParallelize] // This test will not be run in parallel
        public void TestPrice_5PercentTax() => //YourTestHere?;

        [TestMethod]
        [DoNotParallelize] // This test will not be run in parallel
        public void TestPrice_10PercentTax() => //YourTestHere?;            

        [TestMethod]
        [DoNotParallelize] // This test will not be run in parallel
        public void TestPrice_NoTax() => //YourTestHere?;

        [TestMethod]
        public void TestInventory_Add10Items() => //YourTestHere?;

        [TestMethod]
        public void TestInventory_Remove10Items() => //YourTestHere?;
    }
}

可以在此处找到更详细的信息MSTest v2 at meziantou.net

我强烈建议至少快速通读 link,因为这可能会帮助您解决和理解并行或顺序测试 运行 的问题。

我想提供一个我开始但没有追求的潜在解决方案。

首先,我制作了一个 class 可以用作我的测试方法的属性。

[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple =true)]
public class NoParallel : Attribute
{
    public NoParallel(string nonParallelGroupName)
    {
        SetName = nonParallelGroupName;
    }

    public string SetName { get; }
}

然后我去将它添加到我的测试方法中会发生冲突。

[NoParallel("Tax")]
public void TestPrice_5PercentTax();

[NoParallel("Tax")]
public void TestPrice_10PercentTax();

[NoParallel("Tax")]
public void TestPrice_NoTax();

// This test doesn't care
public void TestInventory_Add10Items();

// This test doesn't care
public void TestInventory_Remove10Items();

我给了我的测试 class 一个由他们的名字作为键的互斥锁的静态字典。

private static Dictionary<string, Mutex> exclusiveCategories = new Dictionary<string, Mutex>();

最后,使用助手获取测试方法具有的所有 "NoParallel" 个字符串...

public static List<string> NonparallelSets(this TestContext context, ContextHandler testInstance)
{
    var result = new List<string>();

    var testName = context.TestName;
    var testClassType = testInstance.GetType();
    var testMethod = testClassType.GetMethod(testName);

    if (testMethod != null)
    {
        var nonParallelGroup = testMethod.GetCustomAttribute<NoParallel>(true);

        if (nonParallelGroup != null)
        {
            result = nonParallelGroups.Select(x => x.SetName).ToList();
        }
    }

    result.Sort();
    return result;
}

...我设置了 TestInitialize 和 TestCleanup 以使具有匹配的 NoParallel 字符串的测试按顺序执行。

[TestInitialize]
public void PerformSetup()
{
    // Get all "NoParallel" strings on the test method currently being run
    var nonParallelSets = testContext.NonparallelSets(this);

    // A test can have multiple "NoParallel" attributes so do this for all of them
    foreach (var setName in nonParallelSets)
    {
        // If this NoParallel set doesn't have a mutex yet, make one
        Mutex mutex;
        if (exclusiveCategories.ContainsKey(setName))
        {
            mutex = exclusiveCategories[setName];
        }
        else
        {
            mutex = new System.Threading.Mutex();
            exclusiveCategories[setName] = mutex;
        }

        // Wait for the mutex before you can run the test
        mutex.WaitOne();
    }
}

[TestCleanup]
public void PerformTeardown()
{
    // Get the "NoParallel" strings on the test method again
    var nonParallelSets = testContext.NonparallelSets(this);

    // Release the mutex held for each one
    foreach (var setName in nonParallelSets)
    {
        var mutex = exclusiveCategories[setName];
        mutex.ReleaseMutex();
    }
}

我们决定不继续这样做,因为它真的不值得付出努力。最终我们决定将不能 运行 的测试拉到他们自己的测试 class 中,并按照 H.N 的建议用 [DoNotParallelize] 标记它们。