有没有办法在MSTests中按顺序运行单元测试?

46

我正在处理一项主要是单线程、单用户的应用程序。有一些工作线程在不时地运行,而它们只使用线程安全的对象和类。实际上,单元测试是使用多个线程进行测试(专门为测试而创建),并且测试结果良好。

但是,当测试不安全的业务对象和子系统时,VSTS单元测试会失败。对于它们不是线程安全的情况,这也是可以接受的,因为应用程序就是这样使用它们的。

但是微软测试的“每个TestMethod一个线程”的方法却给我们带来了困扰。我不得不在许多单元测试类中实现对象锁,以确保测试一个接着一个地运行(我并不真正关心它们的顺序,但是我不能让两个测试方法同时操作同一个对象)。

代码看起来像这样:

[TestClass]
public class TestSomeObject
{
   static object turnStile = new object();
...
   [TestMethod]
   public void T01_TestThis()
   {
      lock(turnStile)
      {
      .. actual test code
      }
   }

   [TestMethod]
   public void T02_TestThat()
   {
      lock(turnStile)
      {
      -- actual test code
      }
   }

}

有没有更好/更优雅的方法使得测试按顺序运行?


1
我最终使用了有序测试方法。它运行良好。然而,我在与NAnt构建一起使用时遇到了很多问题。 仅在构建中运行有序测试列表需要在MSTest调用块中使用/testmetadata和/testlist开关。 关于这些的文档描述不太清楚,用温和的说法来形容。我在谷歌上搜索了所有"MSTest /testmetadata /testlist"的示例,但没有效果。然而,这个技巧很简单,我觉得有必要将其回馈给社区,以防其他人遇到同样的问题。1.编辑测试元数据文件(具有.vsmdi扩展名), - Sam Dahan
1
既然你在标签中声称这是单元测试,那么你应该模拟/存根化你的依赖项,尤其是那些不支持多线程的依赖项。 - mxmissile
所以,简短的答案是:不行。因为单元测试之间是不可靠的,你能否承担改变你的测试,以便在每个测试中创建和销毁这样的实例? - BlackTigerX
实际上,答案是肯定的。我接受了弗兰克的建议并创建了一个有序测试。 - Sam Dahan
(我知道这很老)@mxmissile - 实际上,依赖关系可能是测试工具包而不是被测试的单元的函数--即,如果您的测试从基本测试类继承,并且所有重用一个深度嵌套的公共变量/方法(因为最初重用代码更容易,因为所有测试直到“现在”都表现相同),那么在测试类中调整该嵌套变量比复制95%的测试以便您可以使用不同的变量更容易。但是更改一个测试运行会影响并行运行的其他测试... - drzaus
显示剩余2条评论
6个回答

28

太棒了。我知道一定有一种更简单的方式…… :) - Robbie Dee
当使用有序测试时,如何指定 .runsettings 文件?具体来说,我需要访问在 .runsettings 中指定的参数的 TestContext。 - Aaron Hudon
@ajhuddy:听起来是一个很好的新堆栈溢出问题候选人。首先搜索以确保您的问题尚未被提出并回答,并在撰写新问题时引用此问题。 - Robert Harvey
3
窗口底部显示“基于MSTest V2的测试不能在有序测试中使用”。不错。 - dudeNumber4

22

您可以使用播放列表

右键单击测试方法 -> 添加到播放列表 -> 新建播放列表

然后您可以指定执行顺序 在此输入图片描述


我觉得这是对这个问题最好的答案。 - user969153
1
工作效果最佳,并且在不到一分钟的时间内设置非常容易。 - Ruan
有没有办法在批处理文件的命令行中执行播放列表? - StackTrace

19

有一种名为“有序测试”的概念,可以将测试按顺序列出。它更加注重确保特定的顺序,但如果B不等待A完成,我无法看到如何实现这一点。

此外,遗憾的是您的测试相互干扰。可以为每个测试使用设置/拆卸方法,以便最终可能仍然可以使测试彼此隔离。


5
MSTest 运行所有测试都在同一个应用程序域中,导致测试之间产生相互干扰的可能性,这是令人遗憾的。 - Triynko

18

你可以为每个测试执行特别要求一个互斥锁,可以在你想串行化的具体测试中这样做,也可以针对一个类中的所有测试(共享相同的互斥锁字符串)。

对于整个测试类,你可以使用TestInitialize和TestCleanup属性,像这样:

private readonly Mutex testMutex = new Mutex(true, "MySpecificTestScenarioUniqueMutexString");

[TestInitialize]
public void Initialize()
{
    testMutex.WaitOne(TimeSpan.FromSeconds(1));
}

[TestCleanup]
public void Cleanup() {
    testMutex.ReleaseMutex();
}

需要明确的是这不是测试的一个特性,任何锁结构都可以使用。在这种情况下,我正在使用系统提供的 Mutex:

https://msdn.microsoft.com/zh-cn/library/system.threading.mutex(v=vs.110).aspx

你好,能详细说明一下吗?比如,我需要对“......无论什么都共享相同的互斥字符串”的内容进行澄清。此外,你声明了testMutex为Private ReadOnly,但在Initialize和Cleanup中使用了FileTestMutex... 谢谢 - CTZStef
你好!参考不匹配是一个打字错误,我已经修复了。我还添加了一个Mutex类的链接以进行澄清。Mutex是由Microsoft提供的锁定结构,它使用系统范围的字符串进行锁定,因此您希望每组要串行化的东西都是唯一的字符串,这可以是每个测试,或者对于不同的整个测试类。如果您在各处复制/粘贴该字符串,那么每次使用它时都将进行串行化。 还请注意上面的“OrderedTest”建议,如果适合您,那可能是更好的选择。 Mutex(或其他手动锁定)方法很复杂。 - Colin Dabritz
由于我正在使用MSTest v2,所以无法使用有序测试。我正尝试使用你的互斥锁方法,这似乎很简单,但我一直遇到AbandonedMutex异常,难道不应该在其他线程能够进入之前运行TestCleanup并释放互斥锁吗? - Gr-Disarray

9

我最终使用了有序测试方法。它运行良好。

然而,我在与NAnt构建一起使用时遇到了很多问题。只有在构建中运行有序测试列表需要在MSTest调用块中使用/testmetadata和/testlist开关。这些的文档描述不太清楚。我在谷歌上搜索了所有“MSTest /testmetadata /testlist”的例子,但没有效果。

然而,诀窍很简单,我觉得有必要将其归还给社区,以防其他人遇到相同的问题。

  1. 编辑测试元数据文件(具有.vsmdi扩展名),并添加一个新列表到测试列表中(左侧树上的第一个节点)。为其命名,例如'SequentialTests'。
  2. 如果您已经在MSTest调用中使用了/testcontainer开关,请删除它。
  3. 添加一个MSTest开关-> /testmetadata:
  4. 添加一个MSTEst开关 /testlist:SequentialTests(或您使用的任何名称)

然后,MSTest仅运行您创建的测试列表中列出的测试。

如果有更好的方法,我想听听!


2

我使用了有序测试,并在Jenkins上轻松配置了它们,只需使用以下命令:

MSTest /testcontainer:“orderedtestfilename.orderedtest” /resultsfile:“testresults.trx”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接