【问题标题】:Is there a way to calculate elapsed time for test methods ignoring time spent initializing?有没有办法计算测试方法的经过时间而忽略初始化所花费的时间?
【发布时间】:2016-12-01 00:03:34
【问题描述】:

这是一种涉及 StackOverflow 和 SuperUser 之间灰色地带的问题。由于我怀疑答案可能涉及与代码相关的解决方案,例如创造性地使用StopWatch,所以我会在这里问它。

我正在使用 Visual Studio 2013 对依赖于实体框架数据模型的控制器执行单元测试。测试资源管理器在一个简短的列表中很好地展示了我的测试结果以及它们经过的时间。

不过,我发现我的 Controller 单元测试花费的时间比我预期的要长得多。我开始怀疑这是由于我用来创建模拟(使用 Moq,但这无关紧要)实体模型的初始化代码。

果然,显示初始化例程包含在经过的时间中是一件小事。

[TestClass]
public class InitializeTest
{
    [TestInitialize]
    public void Initialize()
    {
        Thread.Sleep(10000);
    }

    [TestMethod]
    public void TestInitializeRuntime()
    {
        Assert.Inconclusive();
    }
}

这在测试资源管理器中产生了以下输出:

这使得由我的模拟实体模型支持的测试的运行时间相当无用,因为初始化代码通常会消耗超过 95% 的测试运行时间。每种方法看起来都很慢,但实际上并非如此。

是否有替代配置或一些创造性的代码使用(如StopWatch,如前所述),允许我只报告测试方法的运行时间,不包括初始化或清理测试所花费的时间?

【问题讨论】:

标签: c# unit-testing visual-studio-2013


【解决方案1】:

我今天发现了一种在 MSTest 中处理昂贵初始化的方法,而不会导致测试报告缓慢。我发布此答案以供考虑,但不接受它,因为它确实具有适度的代码气味。

MSTest 每次运行测试时都会创建一个新的测试类实例。由于这种行为,在实例构造函数中编写的代码每次测试都会出现一次。这与[TestInitialize] 方法的行为类似,但有一个例外:MSTest 在创建测试类的实例和执行[TestInitialize] 例程之前开始计时单元测试.

由于这种特定于 MSTest 的行为,我们可以将自动生成的计时统计信息中应该省略的初始化代码放入构造函数中。

为了说明我的意思,请考虑以下测试和生成的输出。

测试:

public class ConstructorTest
{
    public ConstructorTest()
    {
        System.Threading.Thread.Sleep(10000);
    }

    [TestMethod]
    public void Index()
    {
    }

    [TestMethod]
    public void About()
    {
    }
}

输出:

我的想法:

上面的代码肯定会产生我想要的效果;然而,虽然 appears safe 使用构造函数或 [TestInitialize] 方法来处理初始化,但我必须假设后者存在于这个框架中是有充分理由的。

在某些情况下,在计算中包含初始化时间的报告可能很有用,例如在估计大量测试预计会消耗多少实时时间时。

Rich Turner 关于时间敏感型操作应如何使用带有断言的秒表的讨论也值得认可(并且有我的投票)。另一方面,我将 Visual Studio 提供的自动生成的计时报告视为一种有用的工具,可以识别失控的测试,而无需在每个测试中编写计时样板代码。

总之,我很高兴找到了解决方案,也很欣赏这里讨论的替代方案。

干杯!

【讨论】:

  • 嗨,克布里明顿。我刚刚问了非常相似的问题。您是否设法为这个问题找到任何其他解决方案?出于某种原因,您的解决方案对我不起作用。在我的情况下,在 ConstructorTest(遵循您的代码)中花费的时间显示在结果中,您知道为什么吗?
  • @zdebyman,谢谢你的提问。这是我当时找到的唯一解决方案。我想知道为什么你有不同的行为。在我写这篇文章的时候,我正在使用 VS 2012 和 VS2013。我只是在 VS 2015 中运行它,看看是否有任何变化,它的工作原理如帖子中所述。如果您愿意链接您的类似问题,我很乐意看看。
  • 我的问题是这个:stackoverflow.com/questions/36463238。很奇怪,我在任何 VS(13/15)中都不能有相同的行为。基本上我有这个代码: [TestClass] public class ConstructorTest { public ConstructorTest() { System.Threading.Thread.Sleep(10000); } [TestMethod] public void Index() { } [TestMethod] public void About() { } } 你能看出什么不对吗?
【解决方案2】:

您的测试应该旨在回答问题。诸如以下的问题: 1. 我的代码是否按预期运行? 2. 我的代码是否按预期执行?

但是,与其依赖测试框架的内部工作来为您的代码计时(这是一项特别不适合的任务),不如考虑编写测试来测试特定代码和/或例程的性能。

例如,您可以编写一个测试方法来启动秒表、执行一些工作、停止秒表并测量操作所用的时间。然后,您应该能够断言测试没有超过预期的最大持续时间,如果超过了,您会将其视为失败的测试。

通过这种方式,您不是在测量测试基础架构的不可预测的性能,而是在实际测试代码的性能。

此外,正如 Aravol 建议的那样,您可以通过在 static constructor 中填充您的起订量来预先加载测试设置的成本,因为静态构造函数在 newed 或实例方法执行之前执行。

【讨论】:

    【解决方案3】:

    您遇到的问题是单元测试不允许更改时间或数据输出 - 它们只是执行并完成。

    您可以这样做的一种方法是违反单元测试标准并使用静态引用和静态构造函数来准备您的支持数据 - 虽然技术上没有保证,但 VS 2013 确实在同一个 AppDomain 中执行所有单元测试(尽管通过单独的实例给定的TestClass)

    【讨论】:

      【解决方案4】:

      我不认为在很多情况下实际上需要进行冗长的测试初始化​​。知道发布者的示例中包含了哪些类型的操作会很有趣。

      将 TestInit 的函数分离到 ClassInit 需要一点创意(之前有人建议使用构造函数……类似的东西,但该代码块中的错误报告会完全不同)。例如,如果每个测试都需要从文件中读取的字符串 List,则可以这样拆分:

      1) ClassInit - 读取文件,将字符串捕获到数组中(慢速部分) 2) TestInit - 将数组的元素复制到 List 每个测试都可以访问(快速部分)

      我反对使用静态来尝试解决测试性能问题,它会破坏每个测试之间的隔离。

      我也反对使用 StopWatches 之类的东西来断言自己的性能的测试...运行测试会生成报告,因此该报告的观察者应该识别运行时间过长的测试。此外,如果我们希望自动化测试来测试某项的性能,那不是单元测试,而是性能测试,它可以(应该?)完全不同。

      【讨论】:

        猜你喜欢
        • 2020-03-23
        • 1970-01-01
        • 1970-01-01
        • 2019-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多