我相信大家都知道setUp(@Before)会在任何测试方法执行之前执行,而tearDown(@After)会在测试方法之后执行。
我们也知道Junit会为每个测试方法创建一个Test 实例。
我的问题是,我们可以将setUp方法内容移动到类Constructor并删除setUp方法吗?是否有任何特定的理由保留setUp方法?
答案 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个很好的理由。总结:
有些情况可能更愿意尽可能延迟设置测试装置,在测试用例执行之前。
某些测试用例可能是深度测试用例继承层次结构的一部分。可能最好推迟设置测试夹具,直到构造函数的完整层次结构完成为止。
如果设置代码在setUp()中失败而不是在构造函数中失败,则可以获得更好的诊断。
可用性设计
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()方法在每个测试方法之前调用 。但这在实践中似乎并没有那么有用。
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() 也非常适合用于文档目的,只是因为它可以使代码更易于阅读 。
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)
我认为某些原因应该如下: