单元测试的构造函数运行两次
我有一些测试代码正在初始化一些应该在开始时只做一次的成员。这就是为什么我用的构造:单元测试的构造函数运行两次
[TestFixture]
public class MyTestClass
{
private readonly IUnitTestGeometryProvider m_GeometryProvider;
public MyTestClass()
{
// do some heavy init-op
}
private IEnumerable<TestCaseData> TestCases()
{
yield return new TestCaseData(this.m_GeometryProvider.GetPolyline())
.Throws(typeof(ArgumentException));
}
[TestCaseSource("TestCases")]
public double Check_GetReducedArea_For_Invalid_Arguments(IGeometry theGeom)
{
return theGeom.GetReducedArea();
}
}
我知道这个约定的使用FixtureSetup
-attribute初始化测试,例如从this question on SO。但是我注意到方法TestCases
在用该属性标记的方法之前执行,所以当评估不同的测试用例时碰到NullReferenceException
,因为m_GeometryProvider
目前为null
。
所以我调试了我的代码并在构造函数中设置了一个断点。我注意到,它在任何测试甚至运行之前执行了两次。我假定每个测试用例都有自己的MyTestClass
实例,但由于我有三个不同的测试用例,并且构造函数运行两次,所以这并不能解释它。
由于初始化很重,我只想执行一次。有没有办法保证这一点?我想避免一个static
成员,因为它经常吸引同事大量使用其他static
成员,因为已经有成员。此外,我认为test-init只针对我的MyTestClass
的一个实例,而不是类本身 - 假设只有一个实例。
我正在使用NUnit 2.5.10。
基本上,NUnit保留在任何需要时都可以构建您的夹具对象的权利,而且它可以随时根据需要进行构建。因此,您应该避免在构造函数中进行繁重的初始化操作,特别是如果仅在运行测试时需要初始化。
一般来说,执行测试的初始化应该在TestFixtureSetUp方法中完成,该方法每次执行fixture时只运行一次。但是,使用TestCaseSource属性生成测试用例时,这会更复杂。
由于必须执行TestCases才能创建测试,因此必须先创建对象实例才能创建测试。然后,当测试运行时,会创建另一个实例来运行它们。在你的问题中没有足够的信息来弄清楚为什么有两个“额外”调用到你的构造函数中,但是这可能是由于代码的某些其他方面,或者仅仅是你使用的NUnit的旧版本中的一个错误。你将不得不遍历这些构造函数调用,并检查堆栈跟踪以查看调用它们的内容。
您的m_GeometryProvider成员是否在TestCases方法以外的其他地方使用?如果不是,可以通过将其设置为在该方法中创建的临时字段来简化。然后,您可以通过使TestCases方法为静态来消除额外的构造函数调用。你说你不喜欢那个,但这是我们推荐的。事实上,在NUnit 3.0及更高版本中,它必须是静态的。
如果除了创建几何提供程序之外,还有其他初始化需要运行测试,那么应该使用TestFixtureSetUp方法。
目前geometryProvider仅用于该方法,但随着更多测试的实施,我还需要从这些方法中访问它。不过非常感谢这个答案,它清楚了我已经假设的一些事情 - 特别是在3.x方法需要静态的事实。 – HimBromBeere
我试图调试它,但对于这两个调用,我得到的同一个stacktrace只提到一个方法('JetBrains.ReSharper.UnitTestRunner.nUnit26.dll!JetBrains.ReSharper.UnitTestRunner.nUnit26.DelegatingTrunner.RunTests(...)' )。我添加了第二个方法,标记为名为'OtherTestCases'的'TestCaseSourceAttribute',该方法产生给被称为**三次**的构造函数。显然,引擎也会为每个'TestCaseSource'调用构造函数一次。 – HimBromBeere
啊......你之前没有提到过Resharper。希望它没有区别。 是的,您的构造函数将被调用__each使用___包含在方法中的非静态TestCaseSource。一些版本的NUnit尝试缓存单个实例,但这是很久以前我不记得哪一个。 :-) – Charlie
不要使用构造函数进行初始化。使用'TestFixtureSetup'属性进行大量初始化,而不是构造函数。每个测试初始化方法都用'SetUp'属性标记。 BTW 2.5.10很旧。目前的版本是3.7 –
@PanagiotisKanavos我已经解释了为什么这对我不起作用。问题在于,使用该属性后,标记为该方法的方法在* TestCases方法后运行很远,导致后者运行到NRE。 – HimBromBeere
@PanagiotisKanavos它会引发错误,因为TestCases在安装之前运行 –