此外,我们也知道Junit会为每个测试方法创建一个Test实例。
我的问题是,我们能否将 setUp 方法的内容移到类构造函数中并删除 setUp 方法?保留setUp方法的具体原因是否存在?
答案:可以将 setUp 方法的内容移动到类的构造函数中,并删除 setUp 方法。 但是需要注意的是,如果这样做,每个测试方法都将共享同一个测试实例,并且可能会影响测试结果的准确性。因此,如果测试方法之间有依赖关系或者需要重置状态,则必须使用 setUp 方法来确保测试的独立性和可重复性。
这篇(旧的)JUnit最佳实践文章是这样说的:
不要使用测试用例构造函数来设置测试用例
在构造函数中设置测试用例不是一个好主意。请考虑:
public class SomeTest extends TestCase public SomeTest (String testName) { super (testName); // Perform test set-up } }
假设在执行设置时,设置代码抛出一个
IllegalStateException
异常。作为响应,JUnit 会抛出一个AssertionFailedError
异常,表示无法实例化测试用例。以下是 resulting stack trace 的示例:
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 EntityManager对象进行数据库测试,因此这变成了许多对象和大量的内存,而且在测试进行到一半时,我们会收到OutOfMemory异常,这就产生了“内存泄漏”的问题。
在我看来,最佳实践是使用setUp和tearDown来注入你的依赖项,并将所有类引用设置为空值。这将使你的测试运行更快,节省很多麻烦!
希望你从我们的错误中吸取教训 :)
以下是三个很好的理由:
有些情况下可能更喜欢在测试用例执行之前尽可能晚地设置测试夹具。
某些测试用例可能是深层次的测试用例继承结构的一部分。在整个构造函数层次结构完成之前延迟设置测试夹具可能更可取。
如果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极限编程-4.6 设置和拆卸
http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/
你可能会想知道为什么要编写setUp()方法,而不是在测试用例的构造函数中初始化字段。毕竟,由于每个测试方法都会创建测试用例的新实例,因此构造函数总是在setUp()之前被调用。在绝大多数情况下,您可以使用构造函数代替setUp(),而不会产生任何副作用。
但是如果您的测试用例是更深层次的继承层次结构的一部分,则可能希望将对象初始化推迟到衍生[测试]类的实例完全构造完成时。这是您可能希望使用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
方法所需的。但是依赖注入只能在对象构造完成后运行。
@BeforeClass
,如果您想进行全局设置,则设置和拆卸是可选的。setUp
所做的工作。 - Nader Shirazie我认为应该有以下一些原因: