相信大家都知道 setUp(@Before) 会在任何测试方法之前执行,而 tearDown(@After) 会在测试方法之后执行。
我们也知道 Junit 将为每个测试方法创建一个 Test 实例。
我的问题是我们可以将 setUp 方法内容移动到类 Constructor 并删除 setUp 方法吗?是否有任何特定理由保留 setUp 方法?
这篇(旧的)JUnit 最佳实践文章是这样描述的:
不要使用测试用例构造函数来设置测试用例
在构造函数中设置测试用例不是一个好主意。考虑:
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
)以及从哪里抛出。这使得解释测试设置的失败变得容易得多。
在工作中,我们发现了一些相当有趣的东西,可以回答您的问题。当您运行一个测试套件,尤其是一大组测试(200+)时,JUnit 开始使用大量内存,这是因为所有测试都是在运行任何实际测试方法之前实例化的。
我们遇到了“内存泄漏”,因为我们使用 Spring 为我们的数据库测试连接了一些 JPA EntiryManager 对象,这变成了很多对象和大量内存,大约在测试进行到一半时,我们得到了 OutOfMemory 异常.
恕我直言,最佳实践是使用 setUp 和 tearDown 来注入您的依赖项并清除任何和所有类引用,这将使您的测试运行得更快并为您节省很多头痛!
希望你能从我们的错误中吸取教训:)
这里有 3 个很好的理由。总之:
在某些情况下,可能更愿意将设置测试装置尽可能推迟到测试用例执行之前。
一些测试用例可能是深层测试用例继承层次结构的一部分。最好推迟设置测试装置,直到构造器的完整层次结构完成。
如果设置代码在 setUp() 中失败而不是在构造函数中失败,您将获得更好的诊断。
可用性设计
http://www.artima.com/weblogs/viewpost.jsp?thread=70189
...正如 Elliotte Rusty Harold 所说,如果您要为每个测试方法创建一个新的 TestCase 实例,“为什么还要麻烦 setUp() 方法?” 您可以只使用 TestCase 构造函数。
我听说 Bruce Eckel 指出,在 setUp() 中创建夹具与在 TestCase 构造函数中创建夹具之间存在细微差别。JUnit 预先创建所有 TestCase 实例,然后为每个实例调用 setup()、测试方法和 tearDown()。换句话说,细微的区别是构造函数都是在前面批量调用的,而 setUp() 方法是在每个测试方法之前调用的。但这在实践中似乎并没有那么有用。
ETutorial 的 Java Extreme Programming - 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) 以及从何处引发。这使得解释测试设置的失败变得容易得多。
自定义运行器,例如SpringJUnit4ClassRunner
可能需要在构造函数和@Before
方法之间运行一些代码。在这种情况下,运行程序可能会注入一些@Before
方法需要的依赖项。但是依赖注入只能在对象构造完成后运行。
您需要这样做的原因是,对于许多测试,您通常需要在每次测试之前初始化状态,以便测试都可以对它们正在运行的开始状态做出假设。
假设您的测试类包装,说数据库访问。每次测试后,您都希望删除测试对数据库所做的任何更改 - 如果您不这样做,每个测试都会针对稍微修改的数据库运行。此外,如果先前测试的某些子集失败,任何给定的测试都可能会看到一组不同的更改。例如,假设 test1 进行了插入,test2 会检查您是否准确地读取了表大小。第 1 天,test1 失败,0 是正确的。第2天,test1成功,1正确?
@BeforeClass
顺便说一句,如果您想进行全局设置,junit 也支持,并且设置和拆卸是可选的。
我认为某些原因应该如下: