最佳实践:在setUp()或声明中初始化JUnit类字段?

我应该像这样初始化类字段吗?

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 } } 

我倾向于使用第一种forms,因为它更简洁,并允许我使用最终字段。 如果我不需要使用setUp()方法进行设置,我还应该使用它,为什么?

澄清: JUnit将根据testing方法实例化testing类。 这意味着list将在每个testing中创build一次,无论我在哪里声明它。 这也意味着testing之间没有时间依赖关系。 所以看起来使用setUp()没有任何优势。 然而,JUnit的常见问题有很多例子,在setUp()中初始化一个空的集合,所以我认为必须有一个原因。

如果您对JUnit常见问题解答中的例子(如基本testing模板)有疑问 ,我认为最好的做法是在testing中的应该在您的setUp方法(或testing方法)中实例化, 。

当JUnit的例子在setUp方法中创build了一个ArrayList的时候,他们继续testing那个ArrayList的行为,例如testIndexOutOfBoundException,testEmptyCollection等等。 有人撰写课程并确保其正确运作。

testing自己的类时,你应该也可以做同样的事情:在setUp或者testing方法中创build你的对象,这样你以后可以得到合理的输出。

另一方面,如果在testing代码中使用Java集合类(或其他库类),则可能不是因为要testing它,而只是testing工具的一部分。 在这种情况下,您可以安全地假定它按预期工作,所以在声明中初始化它不会成为问题。

值得一提的是,我在一个相当大的,几年前的TDD开发的代码库上工作。 我们习惯性地在testing代码中声明他们的声明中的东西,在我参与这个项目的一年半里,它从来没有造成任何问题。 所以至less有一些传闻证据表明这是一个合理的事情。

我开始自己挖掘,发现使用setUp()一个潜在优势。 如果在执行setUp()期间抛出任何exception,JUnit将打印一个非常有用的堆栈跟踪。 另一方面,如果在构造对象期间抛出一个exception,那么错误信息就是说JUnit不能实例化testing用例,而且你也看不到发生失败的行号,可能是因为JUnit使用了reflection来实例化testing类。

这一切都不适用于创build一个空集合的例子,因为这永远不会抛出,但这是setUp()方法的一个优点。

除了Alex B的回答。

甚至需要使用setUp方法来实例化处于特定状态的资源。 在构造函数中这样做不仅是时间问题,而且由于JUnit运行testing的方式,每个testing状态在运行之后都会被擦除。

JUnit首先为每个testing方法创buildtestClass的实例,并在每个实例创build后开始运行testing。 在运行testing方法之前,运行其设置方法,可以准备一些状态。

如果数据库状态将在构造函数中创build,那么在运行每个testing之前,所有实例都将立即实例化db状态。 从第二个testing开始,testing将以脏状态运行。

JUnits生命周期:

  1. 为每个testing方法创build一个不同的testing类实例
  2. 对每个testing类实例重复:调用setup +调用testmethod

用两种testing方法进行一些testing,你会得到:(number是散列码)

  • 创build新实例:5718203
  • 创build新的实例:5947506
  • 安装程序:5718203
  • TestOne:5718203
  • 设置:5947506
  • TestTwo:5947506

在JUnit 3中,在运行任何testing之前 ,您的字段初始值设定项将按每个testing方法运行一次。 只要你的字段值内存小,花费很less的时间,并且不影响全局状态,使用字段初始值设定在技术上是好的。 但是,如果这些操作不起作用,那么在第一次testing运行之前,可能会花费大量内存或时间来设置字段,甚至可能导致内存不足。 由于这个原因,许多开发人员总是在setUp()方法中设置字段值,即使不是绝对必要的,它总是安全的。

请注意,在JUnit 4中,testing对象的初始化恰好在testing运行之前发生,所以使用字段初始化器更安全,并且推荐使用样式。

你的情况(创build一个列表)在实践中没有区别。 但通常最好使用setUp(),因为这有助于Junit正确报告exception。 如果在Test的构造函数/初始化程序中发生exception,那就是testing失败 。 但是,如果在安装过程中发生exception,自然可以将其视为设置testing的一些问题,并且junit会适当地报告。

在JUnit 4中:

  • 对于正在testing ,在@Before方法中进行初始化以捕获失败。
  • 对于其他类 ,在声明中初始化…
    • …为了简洁起见,并final确定问题所述的领域,
    • …除非是复杂的初始化可能会失败,在这种情况下使用@Before来捕捉失败。
  • 对于全局状态 (特别是像数据库一样的慢初始化 ),使用@BeforeClass ,但要小心testing之间的依赖关系。
  • 单个testing中使用的对象的初始化当然应该在testing方法本身中完成。

@Before方法或testing方法中进行初始化,可以让您获得更好的错误报告。 这对于实例化待testing类(可能会中断)特别有用,但对于调用外部系统(如文件系统访问(“文件未find”)或连接到数据库(“连接被拒绝”))也很有用。

有一个简单的标准是可以接受的 ,因为复杂的编码规则很难遵循,所以总是使用@Before (清除错误,但是冗长)或者总是在声明中初始化(简洁但给出混乱的错误),这不是什么大问题。

setUp中进行初始化是JUnit 3的一个遗留问题,所有testing实例都是急切地初始化的,如果执行昂贵的初始化,会导致问题(速度,内存,资源耗尽)。 因此,最好的做法是在setUp进行昂贵的初始化,这只在testing执行时才运行。 这不再适用,所以使用setUp就不那么必要了。

这总结了几个其他答复,埋葬的主要,由克雷格体育Motlin(问题本身和自我回答),Moss Collum(testing类)和dsaff。

我更喜欢首先可读性,最经常不使用安装方法。 当一个基本的设置操作需要很长时间并在每个testing中重复时,我会例外。
此时,我将该function移入使用@BeforeClass批注(稍后优化)的设置方法中。

使用@BeforeClass设置方法进行优化的示例:对于某些数据库functiontesting,我使用了dbunit。 设置方法负责将数据库置于已知状态(非常慢… 30秒 – 取决于数据量2分钟)。 我在用@BeforeClass注释的setup方法中加载这些数据,然后针对同一组数据运行10-20个testing,而不是在每个testing中重新加载/初始化数据库。

使用Junit 3.8(扩展TestCase,如您的示例所示)需要编写更多的代码,而不仅仅是添加注释,但“在类设置之前运行一次”仍然是可能的。

由于每个testing都是独立执行的,只要有一个新的对象实例,除了在setUp()和个别test和tearDown()之间共享外,Test对象没有任何内部状态。 这是除了其他原因之外的原因之一,使用setUp()方法是很好的。

注意:JUnittesting对象保持静态是一个好主意! 如果您在testing中使用静态variables来进行除跟踪或诊断之外的任何其他操作,那么您将失去JUnit的部分function,即testing可以(可以)以任意顺序运行,每个testing都以新鲜,干净的状态。

使用setUp()的好处在于,您不必在每个testing方法中剪切并粘贴初始化代码,并且在构造函数中没有testing设置代码。 在你的情况下,没有什么区别。 只要创build一个空列表,可以安全地完成,因为它是一个简单的初始化。 但是,正如你和其他人所指出的那样,任何可能抛出Exception东西都应该在setUp()完成,以便在失败时得到诊断堆栈转储。

在你的情况下,你只是在创build一个空的列表,我会以同样的方式build议:在声明点分配新列表。 特别是因为这样,如果这对你的testing类是有意义的,你可以selectfinal的标记。