setUp / tearDown(@ Before / @ After)为什么我们在JUnit中需要它们?

时间:2010-09-06 02:29:34

标签: java junit junit4 junit3

我相信大家都知道setUp(@Before)会在任何测试方法执行之前执行,而tearDown(@After)会在测试方法之后执行。

我们也知道Junit会为每个测试方法创建一个Test 实例

我的问题是,我们可以将setUp方法内容移动到类Constructor并删除setUp方法吗?是否有任何特定的理由保留setUp方法?

6 个答案:

答案 0 :(得分:58)

这篇(旧)JUnit best practices文章的内容如下:

  

不要使用测试用例构造函数来设置测试用例

     

在中设置测试用例   构造函数不是一个好主意。   考虑:

public class SomeTest extends TestCase
   public SomeTest (String testName) {
      super (testName);
      // Perform test set-up
   }
}
     

想象一下,在表演时   设置,设置代码抛出一个   IllegalStateException。作为回应,   JUnit会抛出一个   AssertionFailedError,表明这一点   测试用例不可能   实例化。这是一个例子   产生的堆栈跟踪:

junit.framework.AssertionFailedError: Cannot instantiate test case: test1   
    at junit.framework.Assert.fail(Assert.java:143)
    at junit.framework.TestSuite.runTest(TestSuite.java:178)
    at junit.framework.TestCase.runBare(TestCase.java:129)
    at junit.framework.TestResult.protect(TestResult.java:100)
    at junit.framework.TestResult.runProtected(TestResult.java:117)
    at junit.framework.TestResult.run(TestResult.java:103)
    at junit.framework.TestCase.run(TestCase.java:120)
    at junit.framework.TestSuite.run(TestSuite.java, Compiled Code)
    at junit.ui.TestRunner2.run(TestRunner.java:429)
     

这个堆栈跟踪证明了   无信息;它只表明了这一点   测试用例不可能   实例化。它没有详细说明   原始错误的位置或地点   起源。缺乏信息使得   很难推断出例外情况   根本原因。

     

而不是在中设置数据   构造函数,执行测试设置   覆盖setUp()。任何例外   报告在setUp()内投掷   正确。比较这个堆栈跟踪   与前面的例子:

java.lang.IllegalStateException: Oops
    at bp.DTC.setUp(DTC.java:34) 
    at junit.framework.TestCase.runBare(TestCase.java:127)
    at junit.framework.TestResult.protect(TestResult.java:100)
    at junit.framework.TestResult.runProtected(TestResult.java:117)
    at junit.framework.TestResult.run(TestResult.java:103)
    ...
     

此堆栈跟踪更多   信息;它显示了哪个例外   被抛出(IllegalStateException)和   来自哪里。这使得它变得容易多了   解释测试设置的失败。

答案 1 :(得分:24)

在工作中我们发现了一些非常有趣的东西来回答你的问题。当你运行一个测试套件,特别是一大组测试(200+)时,JUnit开始使用大量内存,这是因为在运行任何实际测试方法之前所有测试都是实例化的。

我们遇到了“内存泄漏”,因为我们使用Spring连接一些JPA EntiryManager对象进行数据库测试,这变成了大量的对象和大量的内存,大约在我们测试的一半时间内获得OutOfMemory异常。

恕我直言,最佳做法是使用setUp和tearDown来注入你的依赖项,并将任何和所有类引用归零,这将使你的测试运行得更快,并为你节省大量的头痛!

希望你从我们的错误中吸取教训:)

答案 2 :(得分:23)

以下是3个很好的理由。总结:

  1. 有些情况可能更愿意尽可能延迟设置测试装置,在测试用例执行之前

  2. 某些测试用例可能是深度测试用例继承层次结构的一部分。可能最好推迟设置测试夹具,直到构造函数的完整层次结构完成为止。

  3. 如果设置代码在setUp()中失败而不是在构造函数中失败,则可以获得更好的诊断。

  4. 1。推迟设置灯具直到测试用例

    之前

    可用性设计
    http://www.artima.com/weblogs/viewpost.jsp?thread=70189

      

    ......正如Elliotte Rusty Harold所说的那样,如果你要为每个测试方法创建一个新的TestCase实例,“为什么他们会厌烦setUp()方法?”您可以使用TestCase构造函数。

         

    我听说Bruce Eckel指出在setUp()中创建夹具与在TestCase构造函数中创建夹具之间存在之间的一个细微差别。 JUnit 预先创建所有TestCase实例,然后为每个实例创建,调用setup(),test方法和tearDown()。换句话说, 的细微差别在于,构造函数都是先批量调用的,而setUp()方法在每个测试方法之前调用 。但这在实践中似乎并没有那么有用。

    2。在实例化所有测试用例之后,推迟设置灯具

    ETutorial的Java极限编程 - 4.6设置和拆除
    http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/

      

    您可能想知道为什么要编写setUp()方法而不是简单地初始化测试用例的构造函数中的字段。毕竟,由于为每个测试方法创建了一个新的测试用例实例,因此总是在setUp()之前调用构造函数。在绝大多数情况下,您可以使用构造函数而不是setUp()而不会产生任何副作用。

         

    如果您的测试用例是更深层继承层次结构的一部分,您可能希望推迟对象初始化,直到派生[test]类的实例完全构建 。这是一个很好的技术原因,您可能希望使用setUp()而不是构造函数进行初始化。使用setUp()和tearDown() 也非常适合用于文档目的,只是因为它可以使代码更易于阅读

    3。安装失败时更好的诊断

    JUnit最佳实践(JavaWorld)
    http://www.javaworld.com/jw-12-2000/jw-1221-junit.html

      

    在构造函数中设置测试用例不是一个好主意。 ...

         

    想象一下[在测试用例构造函数中完成设置的代码],在执行设置时,安装代码会抛出IllegalStateException。作为响应,JUnit将抛出AssertionFailedError,指示无法实例化测试用例。 ...

         

    这个堆栈跟踪[测试用例构造函数中的设置代码中抛出的异常]证明相当无法提供信息;它只表示无法实例化测试用例。

         

    不是在构造函数中设置数据,而是通过覆盖setUp()来执行测试设置。在setUp()中抛出的任何异常都会正确报告。 ...

         

    这个堆栈跟踪[在setUp()方法而不是测试用例构造函数中抛出的异常]提供了更多信息;它显示抛出了哪个异常(IllegalStateException)以及从哪里开始。 这使得解释测试设置失败变得容易得多。

答案 3 :(得分:6)

SpringJUnit4ClassRunner等自定义运行器可能需要在构造函数和@Before方法之间运行一些代码。在这种情况下,跑步者可能会注入@Before方法所需的一些依赖性。但依赖注入只能在构造对象后运行。

答案 4 :(得分:3)

您需要这样做的原因是,对于许多测试,您通常需要在每次测试之前初始化状态,以便测试可以对它们正在运行的起始状态做出假设。

假设您的测试类包装,比如数据库访问。在每次测试之后,您都希望删除测试对数据库所做的任何更改 - 如果您不这样做,则每个测试都会针对略微修改的数据库运行。此外,如果先前测试的某些子集失败,则任何给定测试可能会看到不同的更改集。例如,假设test1执行插入操作,test2检查您是否正在准确读取表大小。第1天,test1失败,0正确。第2天,test1成功,1是正确的?

BTW,如果你想进行全局设置,junit还支持@BeforeClass,并且设置和拆卸是可选的。

答案 5 :(得分:-4)

我认为某些原因应该如下:

  1. 如果你将@Before内容移动到Constructor,那很好,但@After内容你可以移动吗?
  2. Constructor和@ Before / @ After之间的区别在于构造函数应该用于实例化一些类,@ Before / @ After用于准备测试用例资源。