同步多个单元测试程序集

时间:2019-06-30 11:54:26

标签: c# unit-testing async-await synchronization mstest

我有Common类的程序集UnitTestSemaphoreLocker

public static class UnitTestSemaphoreLocker
{
    private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);

    public static void Lock([NotNull] Action work)
    {
        if (work == null) throw new ArgumentNullException(nameof(work));

        Semaphore.Wait();
        try
        {
            work();
        }
        finally
        {
            Semaphore.Release();
        }
    }

    public static async Task LockAsync([NotNull] Func<Task> worker)
    {
        if(worker == null) throw new ArgumentNullException(nameof(worker));

        await Semaphore.WaitAsync();
        try
        {
            await worker();
        }
        finally
        {
            Semaphore.Release();
        }
    }
}

我有两个单元测试程序集,可以说TestAssemblyATestAssemblyB

两个程序集中的单元测试使用一种资源,每次仅支持一个调用。 因此,我希望您使用我的UnitTestSemaphoreLocker来锁定单元测试,并使其一一执行:

[TestMethod]
public void ConstructionTest()
{
    UnitTestSemaphoreLocker.Lock(() =>
    {
       //test body
    });
}

对于一个程序集中的测试来说,它工作得很好,但似乎其中两个创建了我自己的公共静态UnitTestSemaphoreLocker类的实例。

有什么方法可以同步两个程序集的单元测试?同时,其中一部分必须使用async/await

1 个答案:

答案 0 :(得分:2)

根据观察到的行为,Visual Studio测试运行程序(VSTest)并行执行多个测试程序集。它启动多个进程(每个CPU内核最多​​一个)。每个此类进程都包含其自己的CLR副本,每个副本都加载一些AppDomain的副本,当然还加载测试程序集的单独实例。

所有这些都意味着不共享静态成员,并且由于SemaphoreSlim(依赖于CLR同步原语),同步不能在多个CLR实例上起作用。

我可以想到两种可能的解决方案:

解决方案1:跨进程同步

SemaphoreSlim替换为Semaphore。后者可以初始化为机器范围内的命名信号量,从而允许多个进程在由字符串名称标识的同一信号量实例之间进行同步。参见an example here。有关更多信息,另请参见Semaphore and SemaphoreSlim

如果只有很少的测试需要同步,那么这可能是一个很好的解决方案。但是,如果必须同步大多数或所有测试,则运行并行测试过程毫无意义-请参阅解决方案2。

解决方案2:禁用并行测试过程

您可以通过<MaxCpuCount>文件中的.runsettings元素来控制并行进程的数量。将其值设置为1应该会禁用并行进程:

<RunSettings>
  <RunConfiguration>
    <MaxCpuCount>1</MaxCpuCount>
  </RunConfiguration>
</RunSettings>

有关更多详细信息,请参见Configure unit tests by using a .runsettings file

这并不是说您无法并行化测试。 MSTest具有名为“组装内并行测试执行”(IAP)的功能,可以通过包含以下组装级属性(通常在 AssemblyInfo.cs 中)来启用该功能:

[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]

与生成进程的VSTest运行程序相反,IAP生成多个线程,所有线程共享同一AppDomain。在这种情况下,静态成员只有一个副本,并且与SemaphoreSlim的基于CLR的同步也适用。

此信息中有关IAP的更多信息:MSTest V2: in-assembly parallel test execution