我应该在这样的声明中初始化类字段吗?
public class SomeTest extends TestCase { private final List list = new ArrayList(); public void testPopulateList() { // Add stuff to the list // Assert the list contains what I expect } }
或者像setUp()这样?
public class SomeTest extends TestCase { private List list; @Override protected void setUp() throws Exception { super.setUp(); this.list = new ArrayList(); } public void testPopulateList() { // Add stuff to the list // Assert the list contains what I expect } }
我倾向于使用第一种形式,因为它更简洁,并允许我使用最终字段.如果我不需要使用setUp()方法进行设置,我是否还应该使用它,为什么?
澄清:
JUnit将根据每种测试方法实例化一次测试类.这意味着list
每次测试都会创建一次,无论我在哪里声明它.这也意味着测试之间没有时间依赖关系.因此,使用setUp()似乎没有任何优势.但是JUnit FAQ有许多例子可以在setUp()中初始化一个空集合,所以我认为必须有一个原因.
如果你特别想知道JUnit FAQ中的例子,比如基本的测试模板,我认为最好的做法是在你的setUp方法(或测试方法)中实例化测试中的类.
当JUnit示例在setUp方法中创建一个ArrayList时,它们都会继续测试该ArrayList的行为,例如testIndexOutOfBoundException,testEmptyCollection等.有人写一个班级并确保它正常运作.
在测试自己的类时,您可能应该这样做:在setUp或测试方法中创建对象,这样如果稍后打破它,您将能够获得合理的输出.
另一方面,如果在测试代码中使用Java集合类(或其他类库),则可能不是因为您想要测试它 - 它只是测试夹具的一部分.在这种情况下,您可以安全地假设它按预期工作,因此在声明中初始化它不会是一个问题.
为了它的价值,我开发了一个相当大,几年,TDD开发的代码库.我们习惯性地在测试代码的声明中初始化事物,并且在我参与这个项目的一年半中,它从未引起过任何问题.所以至少有一些轶事证据表明它是合理的.
我开始挖掘自己,我找到了使用的一个潜在优势setUp()
.如果在执行期间抛出任何异常setUp()
,JUnit将打印一个非常有用的堆栈跟踪.另一方面,如果在对象构造期间抛出异常,则错误消息只是说JUnit无法实例化测试用例,并且您没有看到发生故障的行号,可能是因为JUnit使用反射来实例化测试类.
这些都不适用于创建空集合的示例,因为它永远不会抛出,但这是该setUp()
方法的一个优点.
除了Alex B的回答.
甚至需要使用setUp方法来实例化某种状态的资源.在构造函数中这样做不仅是时机的问题,但由于方式的JUnit运行测试,每个测试的状态将运行一个后被删除.
JUnit首先为每个测试方法创建testClass的实例,并在创建每个实例后开始运行测试.在运行测试方法之前,运行其设置方法,其中可以准备一些状态.
如果将在构造函数中创建数据库状态,则在运行每个测试之前,所有实例都会在彼此之后立即实例化db状态.从第二次测试开始,测试将以脏状态运行.
JUnits生命周期:
为每个测试方法创建不同的testclass实例
对每个testclass实例重复:调用setup +调用testmethod
在测试中使用两种测试方法进行一些记录:(number是哈希码)
创建新实例:5718203
创建新实例:5947506
设置:5718203
TestOne:5718203
设置:5947506
TestTwo:5947506
在JUnit 4中:
对于Class Under Test,在@Before
方法中初始化以捕获故障.
对于其他类,请在声明中初始化...
......为了简洁,并标记字段final
,正如问题中所述,
...除非复杂的初始化可能会失败,在这种情况下使用@Before
,以捕获失败.
对于全局状态(尤其是慢速初始化,如数据库),请使用@BeforeClass
,但要注意测试之间的依赖关系.
在单个测试中使用的对象的初始化当然应该在测试方法本身中完成.
在@Before
方法或测试方法中初始化可以让您更好地报告故障.这对于实例化被测试类(您可能会破坏)特别有用,但对于调用外部系统也很有用,例如文件系统访问("找不到文件")或连接到数据库("拒绝连接").
这是可以接受有一个简单的标准,而应使用@Before
(明确的错误,但详细)或声明总是初始化(简洁但给人混乱的错误),因为复杂的编码规则是难以遵循,这是不是一个大问题.
初始化setUp
是JUnit 3的遗留物,其中所有测试实例都是急切初始化的,如果进行昂贵的初始化,会导致问题(速度,内存,资源耗尽).因此,最佳实践是进行昂贵的初始化setUp
,这仅在执行测试时运行.这不再适用,因此使用起来要少得多setUp
.
这总结了其他几个埋葬lede的回复,特别是Craig P. Motlin(问题本身和自我答案),Moss Collum(被测试的课程)和dsaff.
在JUnit 3中,在运行任何测试之前,每个测试方法将运行一次字段初始值设定项.只要你的字段值在内存中很小,占用很少的设置时间,并且不影响全局状态,使用字段初始化器在技术上是很好的.但是,如果这些不成立,您可能最终会在第一次测试运行之前消耗大量内存或设置字段,甚至可能耗尽内存.出于这个原因,许多开发人员总是在setUp()方法中设置字段值,即使它不是绝对必要的,它始终是安全的.
请注意,在JUnit 4中,测试对象初始化恰好在测试运行之前发生,因此使用字段初始值设定项更安全,并且建议使用样式.
在你的情况下(创建一个列表),在实践中没有区别.但通常最好使用setUp(),因为这将有助于Junit正确报告异常.如果测试的构造函数/初始化程序中发生异常,则表示测试失败.但是,如果在安装过程中发生异常,很自然会将其视为设置测试时的一些问题,并且junit会对其进行适当的报告.
我更喜欢可读性,最常见的是不使用设置方法.当基本设置操作需要很长时间并在每次测试中重复时,我会例外.
此时,我使用@BeforeClass
注释将该功能移动到设置方法中(稍后进行优化).
使用@BeforeClass
setup方法进行优化的示例:我使用dbunit进行一些数据库功能测试.设置方法负责将数据库置于已知状态(非常慢...... 30秒 - 2分钟,具体取决于数据量).我在注释的setup方法中加载这些数据,@BeforeClass
然后针对同一组数据运行10-20次测试,而不是在每次测试中重新加载/初始化数据库.
使用Junit 3.8(扩展TestCase,如示例所示)需要编写更多代码,而不仅仅是添加注释,但仍然可以"在类设置之前运行一次".