这是一个踩在StackOverflow和SuperUser之间的灰色区域的问题。由于我怀疑答案可能涉及与代码相关的解决方案,例如创造性地使用StopWatch
,我会在此处提出。
我正在使用Visual Studio 2013对依赖于实体框架数据模型的控制器执行单元测试。测试资源管理器很好地在一个简短的列表中显示我的测试结果以及它们的经过时间。
但是,我发现我的控制器单元测试时间比我预期的要长得多。我开始怀疑这是由于我用来创建一个模拟的初始化代码(使用Moq,但这没关系)实体模型。果然,显示初始化例程包含在已用时间中是一件小事。
[TestClass]
public class InitializeTest
{
[TestInitialize]
public void Initialize()
{
Thread.Sleep(10000);
}
[TestMethod]
public void TestInitializeRuntime()
{
Assert.Inconclusive();
}
}
这在测试资源管理器中产生了以下输出:
这使得由我的模拟实体模型支持的测试所用的时间相当无用,因为初始化代码通常占用测试所用时间的95%以上。每种方法看起来都很慢,实际上并非如此。
是否存在替代配置或某些创造性使用代码(例如StopWatch
,如前所述),这将允许我仅报告测试方法的已用时间,不包括初始化或清理时间测试
答案 0 :(得分:3)
我今天发现了一种在MSTest中处理昂贵初始化的方法,但没有导致测试报告速度慢。我发布这个答案是为了考虑而不接受它,因为它确实有适度的代码味道。
每次运行测试时,MSTest都会创建测试类的新实例。由于这种行为,每个测试都会在实例构造函数中编写一次代码。这与[TestInitialize]
方法类似,只有一个例外:MSTest开始计时单元测试 创建测试类的实例后 执行{{例行公事。
由于这种特定于MSTest的行为,可以在构造函数中放置自动生成的时序统计信息中应该省略的初始化代码。
为了证明我的意思,请考虑以下测试并生成输出。
<强>测试强>
[TestInitialize]
<强>输出:强>
我的想法:
上面的代码肯定会产生我想要的效果;然而,虽然appears safe使用构造函数或public class ConstructorTest
{
public ConstructorTest()
{
System.Threading.Thread.Sleep(10000);
}
[TestMethod]
public void Index()
{
}
[TestMethod]
public void About()
{
}
}
方法来处理初始化,但我必须假设后者存在于此框架中是有充分理由的。
可能存在一种情况,即在计算中包含初始化时间的报告可能很有用,例如在估计应该使用大量测试的实际时间时。
Rich Turner关于时间敏感操作应该如何使用断言停止监视的讨论也值得认可(并且有我的投票)。另一方面,我将Visual Studio提供的自动生成的时序报告看作是一种有用的工具,用于识别失控的测试,而无需在每次测试中编写时序样板代码。
总而言之,我很高兴找到了解决方案并欣赏这里讨论的替代方案。
干杯!
答案 1 :(得分:2)
您的测试应该旨在回答问题。问题如: 1.我的代码是否符合预期? 2.我的代码是否按预期执行?
然而,不是依靠测试框架的内部工作来为代码计时(特别是不适合的任务),而是考虑编写测试特定代码和/或例程性能的测试。
例如,您可以编写一个测试方法,该方法可以启动秒表,执行一些工作,停止秒表并测量操作所需的时间。然后你应该断言测试没有超过预期的最大持续时间,如果确实如此,你会认为它是一个失败的测试。这样您就不会测量测试基础架构的不可预测性能,而是实际测试代码的性能。
另外,正如Aravol建议的那样,您可以通过在static constructor中填充Moq来预先加载测试设置的成本,因为静态构造函数在执行new
或实例方法之前执行。
答案 2 :(得分:0)
您遇到的问题是单元测试不允许更改时间或输出数据 - 它们只是执行和完成。
您可以这样做的一种方法是违反单元测试标准并使用静态引用和静态构造函数来准备您的后备数据 - 虽然在技术上无法保证,但VS 2013会在同一AppDomain中执行所有单元测试(尽管通过单独的实例)给定的TestClass
)
答案 3 :(得分:0)
我不相信有很多实例需要冗长的测试初始化。知道海报的例子中包含了哪种操作会很有趣。
将TestInit的函数分成ClassInit需要一点创造力(之前有人建议使用构造函数......有点相同,但代码块中的错误报告方式会有很大不同)。例如,如果每个测试都需要List&lt;&gt;从文件中读取的字符串,您可以这样拆分:
1)ClassInit - 读取文件,将字符串捕获到数组中(慢速部分) 2)TestInit - 将数组的元素复制到List&lt;&gt;中。每次测试都可以访问(快速部分)
我反对使用静态来试图解决测试性能问题,它会破坏每个测试的相互隔离。
我也反对使用像StopWatches这样的东西来测试他们自己的性能...运行测试会生成一个报告,因此该报告的观察者应该识别运行时间过长的测试。此外,如果我们想要自动化测试来表现某些东西,那不是单元测试,那就是性能测试,它可以(应该?)完全不同。