如何并行运行单元测试(MSTest)?
Posted
技术标签:
【中文标题】如何并行运行单元测试(MSTest)?【英文标题】:How to run unit tests (MSTest) in parallel? 【发布时间】:2011-04-24 10:36:54 【问题描述】:我正在寻找并行运行测试套件的方法。
我知道.testrunconfig
设置。这使您可以多路复用 CPU 的数量。
我想并行运行 1000 个测试。这是有道理的,因为我正在测试一个 Web 服务,所以测试中 90% 的时间都在等待服务响应。
关于如何解决这个问题的任何想法?这些测试是为 VS 编写的,但我愿意在 VS 之外运行它们。
后期编辑:Visual Studio 测试团队已在 VS 2015 Update 1 中添加此内容。请参阅以下 Mark Sowul 的回答。
【问题讨论】:
你在这里谈论 1000 个线程。 对,我想知道是否有一个预构建框架来管理这个。或者如果有人构建了自己的框架。 你需要一个负载模拟器。 MSTest 并不打算用作负载测试。顺便说一句,1000 个线程太疯狂了,你需要做的是研究不同的测试技术,这超出了单元测试的范围。 我不打算执行负载测试。 VS 有一个非常好的模块。我只想并行运行单元测试。好吧,1000太多了? 100 然后:) 重复***.com/questions/921953/… 【参考方案1】:此页面上的大多数答案都忘记提及 MSTest 在单独的程序集中并行化测试。你必须将你的单元测试拆分成多个 .dll 来并行化它。
但是!最新版本 - MSTest V2 - 现在 CAN 并行化“装配内”(耶!)您只需要在测试项目中安装几个 nuget 包 - TestFramework 和 TestAdapter - 就像这里描述的 @987654321 @
然后只需将其添加到您的测试项目中
[assembly: Parallelize(Workers = 4, Scope = ExecutionScope.ClassLevel)]
编辑:您还可以在测试方法上使用 [DoNotParallelize]
禁用特定测试的并行执行。
【讨论】:
这个sn-p放在哪里? @Abr 通常程序集范围的属性放在AssemblyInfo.cs
中,但实际上你可以把它放在任何文件中,由你决定。【参考方案2】:
使用from the Visual Studio Team Test Blog方法最多可以得到5个
请记住,使用它可能会出现并发问题,因为 MSTest 不会完全隔离每个测试(静态会继续存在,例如,让原本应该运行一次的代码变得有趣)。
(不知道为什么限制是 5,但如果 parallelTestCount
设置为超过 5,MSTest 将不会并行运行它们。根据下面的 cmets,此规则显然会随着 Visual Studio 2013 改变)
【讨论】:
是的,只是发现很难:) - 我在 8 核上运行我的套件,所有测试都中止了...... 不应限制为总共 5 个,而是 5 个挂起测试。 Bruce Taimana 的评论:“如果清理“挂起”或花费太长时间,我们让它继续清理,但我们并行启动下一个测试。如果达到 5 个“挂起”测试,我们将中止。”阅读整个评论here。 @Anttu 我希望看到一个设置为一次运行超过 5 个的解决方案,并让它实际运行它们而不是失败。每次我尝试为该设置设置超过 5 个时,它都会失败。也许挂起的时间被认为太小了?我真的不知道。 我使用的是 MSTest 12.0.21005.1 (Visual Studio 2013),没有线程限制。【参考方案3】:Visual Studio 2015 Update 1 添加了这个。 https://docs.microsoft.com/visualstudio/releasenotes/vs2015-update1-vs#misc
对于更新 2,测试资源管理器窗格顶部的工具栏中(在“分组”和“搜索”框之间)有一个 UI 切换按钮。
对于更新 1,在 .runsettings 中设置以下内容
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<RunConfiguration>
<MaxCpuCount>0</MaxCpuCount>
</RunConfiguration>
</RunSettings>
MaxCpuCount 的值具有以下语义:
• “n”(其中 1
• 任何其他值的“n”:启动的进程数将与机器上的可用内核数一样多。
【讨论】:
我们有数百个测试的测试DLL,有些可以并行运行,有些不能。有没有一种方法可以归因于安全和不安全的测试类/方法?我通常想选择一个或多个这些巨大的 DLL 并让它撕裂。 要记住的一点是并行性是在类级别上,而不是在测试级别上。我没有看到内置的方式来做你想做的事,但你可以自己做,例如在ClassInitialize
/ClassCleanup
中使用命名互斥锁。不幸的是,这些测试最终可能会阻塞并行性一段时间(例如,所有四个“并行测试”都是使用互斥锁的测试),但这与我认为你能得到的一样好。
测试在容器级别并行化(在 C# 的情况下,即为程序集),因此它将并行运行来自不同程序集的测试。同一程序集中的测试在新模型中按顺序执行。
@jessehouwing 我观察到类级别的并行性具有巨大的速度差异。我认为有一些隐藏的逻辑决定了如何并行化测试。
在某些情况下,即使文件存在并正确命名,文件也不会自动拾取,但您可以配置为从 Visual Studio 菜单“测试”->“配置运行设置”->“选择解决方案”中拾取宽运行设置文件。 (浏览到文件)【参考方案4】:
我发现 C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe
将使用如下所示的.testsettings
文件运行并行测试:
<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="TestSettings1" id="21859d0f-7bdc-4165-b9ad-05fc803c9ee9" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Description>These are default test settings for a local test run.</Description>
<Deployment enabled="false" />
<Execution parallelTestCount="8">
<TestTypeSpecific>
<UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
<AssemblyResolution>
<TestDirectory useLoadContext="true" />
</AssemblyResolution>
</UnitTestRunConfig>
</TestTypeSpecific>
<AgentRule name="Execution Agents">
</AgentRule>
</Execution>
</TestSettings>
参考可以在这里找到http://msdn.microsoft.com/en-us/library/vstudio/jj155796.aspx
【讨论】:
如果您在 Visual Studio 中使用Test Explorer
,它也可以工作。您只需要选择配置了parallelTestCount
的.testsettings
。更多细节在这里:ardalis.com/…顺便说一句:要选择.testsettings
,请单击VS 2013中的TEST
菜单,然后单击Test Settings => Select Test Settings File
。
我从未见过这样的野兽。在 2013 年,我必须创建自己的测试设置文件(在解决方案级别添加项目),然后按照 Igor 的说明对其进行修改以获得并行支持。【参考方案5】:
上述答案确实帮助我澄清了一些事情,但是,John Koerner 的博客中的这一点:https://johnkoerner.com/vs2015/parallel-test-execution-in-visual-studio-2015-update-1-might-not-be-what-you-expect/ 是我们缺少的一点。
“并行测试执行利用机器上的可用内核,并通过将每个可用内核上的测试执行引擎作为一个不同的进程启动,并将其交给一个容器(程序集、DLL 或包含测试的相关工件)来实现执行),值得执行的测试。”
--> “单独的容器位是我缺少的部分。为了让我的测试并行运行,我需要将我的测试拆分为单独的测试程序集。这样做之后,我看到测试在不同的程序集中并行运行。”
是的,我们通过使用他们方便的“并行运行”标志让测试在 VSTS 中并行运行,但这还不够,我们必须将测试拆分为单独的测试项目。当然是按逻辑分组,而不是每个测试项目,这很荒谬
【讨论】:
【参考方案6】:-
确保 DataTable 中的第一列是唯一 ID。
创建一个接受 DataRow 并且不返回任何内容的 AsyncExecutionTask 委托。
使用接受 DataRow 和 AsyncExecutionTask 委托的 AsyncExecutionContext 方法创建一个静态类 (ParallelTesting)。
在静态类中添加静态 BatchStarted 属性。
在静态类中添加静态 AsyncExecutionTests Dictionary 属性。
在 AsyncExecutionContext 方法中添加以下内容:
public static void AsyncExecutionContext(DataRow currentRow, AsyncExecutionTask test)
if(!BatchStarted)
foreach(DataRow row in currentRow.Table)
Task testTask = new Task(()=> test.Invoke(row); );
AsyncExecutionTests.Add(row[0].ToString(), testTask);
testTask.Start();
BatchStarted = true;
Task currentTestTask = AsyncExecutionTests[row[0].ToString()];
currentTestTask.Wait();
if(currentTestTask.Exception != null) throw currentTestTask.Exception;
现在像这样使用这个类:
[TestMethod]
public void TestMethod1()
ParallelTesting.AsyncExecutionContext(TestContext.DataRow, (row)=>
//Test Logic goes here.
);
注意:您必须对异常进行一些修改以使它们正确冒泡(您可能在这里有一个聚合异常,您需要其中的第一个异常)。显示的每个测试执行所需的时间量将不再准确。最后一行完成后,您还需要清理 ParallelTesting 类。
它是如何工作的: 测试逻辑被包装在一个 lambda 中并传递给一个静态类,该静态类将在第一次调用时为每一行测试数据执行一次逻辑(执行第一行) .对静态类的连续调用只是等待预先启动的测试任务完成。
这样,测试框架对TestMethod的每次调用都会简单地收集已经运行的相应测试的测试结果。
可能的改进:
使 AsyncExecutionContext 采用 maxSynchronousTasks 参数。 查看框架如何跨非托管代码移动完整的堆栈跟踪,看看是否可以将 Task.Exception 传递到 Visual Studio 测试框架,而无需重新抛出和破坏堆栈跟踪。【讨论】:
以上是关于如何并行运行单元测试(MSTest)?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Visual Studio 2008 中分析 MsTest 的单元测试?
使用 mstest,我可以针对我支持的每种语言运行我的单元测试套件吗?
是啥导致在单元测试(NUnit 或 MSTest)中从 C# 调用的 C++ 函数与在控制台应用程序中运行的相同代码产生不同的结果?