是否真的有必要在JUnit拆解方法中使对象无效?

我对类似问题的答案很感兴趣。 我认为这是不正确的。 所以我创建了一些测试代码。 我的问题是,这个代码是否certificate/反驳/不确定这一假设,即在拆解方法中使成员变量无效是有用的? 我用JUnit4.8.1测试了它。

JUnit为4个测试中的每个测试创建测试类的新实例。 每个实例都包含一个Object obj。 此obj也作为静态WeakHashMap的键插入。 如果JUnit释放对测试实例的引用,则关联的obj值将被弱引用,因此符合gc条件。 测试试图强制gc。 WeakHashMap的大小将告诉我obj是否是gc’ed。 一些测试使obj变量无效,而其他测试则没有。

import org . junit . Before ; import org . junit . After ; import org . junit . Test ; import java . util . ArrayList ; import java . util . WeakHashMap ; import java . util . concurrent . atomic . AtomicInteger ; import static org . junit . Assert . * ; public class Memory { static AtomicInteger idx = new AtomicInteger ( 0 ) ; static WeakHashMap  map = new WeakHashMap  ( ) ; int id ; Object obj ; boolean nullify ; public Memory ( ) { super ( ) ; } @ Before public void before ( ) { id = idx . getAndIncrement ( ) ; obj = new Object ( ) ; map . put ( obj , new Object ( ) ) ; System . out . println ( "" ) ; } void test ( boolean n ) { nullify = n ; int before = map . size ( ) ; gc ( ) ; int after = map . size ( ) ; System . out . println ( "BEFORE=" + before + "\tAFTER=" + after ) ; } @ Test public void test0 ( ) { test ( true ) ; } @ Test public void test1 ( ) { test ( false ) ; } @ Test public void test2 ( ) { test ( true ) ; } @ Test public void test3 ( ) { test ( false ) ; } @ After public void after ( ) { if ( nullify ) { System . out . println ( "Nullifying obj" ) ; obj = null ; } System . out . println ( "" ) ; } /** * Try to force a gc when one is not really needed. **/ void gc ( ) { ArrayList  waste = new ArrayList  ( ) ; System . gc ( ) ; // only a suggestion but I'll try to force it list : while ( true ) // try to force a gc { try { waste . add ( new Object ( ) ) ; } catch ( OutOfMemoryError cause ) { // gc forced? should have been waste = null ; break list ; } } System . gc ( ) ; // only a suggestion but I tried to force it } } 

我使用命令行界面运行代码(利用-Xmx128k选项增加垃圾收集)并得到以下结果

 . BEFORE=1 AFTER=1 Nullifying obj  . BEFORE=2 AFTER=1  . BEFORE=2 AFTER=1 Nullifying obj  . BEFORE=2 AFTER=1  

Test0 obj无效,在Test1中它是gc’ed。 但Test1 obj没有消失,它在Test2中得到了gc’ed。 这表明无需使用对象。

JUnit 4.x样式测试和测试套件的处理方式与JUnit 3.x测试套件不同。

简而言之,您应该在JUnit3样式的测试中将字段设置为null,但是您不需要在JUnit4样式的测试中

使用JUnit 3.x样式测试, TestSuite包含对其他Test对象(可能是TestCase对象或其他TestSuite对象)的引用。 如果您创建一个包含许多测试的套件,那么对于最外层套件的整个运行,将会有对所有叶子TestCase对象的硬引用。 如果某些TestCase对象在setUp()中分配占用大量内存的对象,并且对这些对象的引用存储在tearDown()中未设置为null字段中,则可能存在内存问题。

换句话说,对于JUnit 3.x样式测试,要运行的测试的规范引用实际的TestCase对象。 在测试运行期间,从TestCase对象可到达的任何对象都将保留在内存中。

对于JUnit 4.x样式测试,要运行的测试的规范使用Description对象。 Description对象是一个值对象,它指定要运行的内容,但不指定如何运行它。 测试由Runner对象运行,该对象获取测试或套件的Description并确定如何执行测试。 甚至向测试侦听器通知测试状态也会使用Description对象。

JUnit4测试用例的默认运行程序JUnit4仅在测试运行期间保持对测试对象的引用。 如果您使用自定义运行器(通过@RunWith注释),该运行器可能会或可能不会长时间保持对测试的引用。

也许您想知道如果在JUnit4风格的套件中包含JUnit3风格的测试类会发生什么? JUnit4将调用new TestSuite(Class) ,它将为每个测试方法创建一个单独的TestCase实例。 跑步者将在测试运行的整个生命周期内保留对TestSuite的引用。

简而言之,如果您正在编写JUnit4样式的测试,请不要担心在拆除时将测试用例的字段设置为null (当然,还有免费资源)。 如果您正在编写在setUp()中分配大对象的JUnit3样式的测试并将这些对象存储在TestCase字段中,请考虑将字段设置为null

它确实没有必要,但它确实有助于垃圾收集器何时需要知道使用了什么变量; null变量几乎被认为是垃圾收集的良好候选者。

不,没有必要。

拆除方法适用于生命周期对象,这些对象喜欢显式关闭,终止,关闭,处置,断开连接,未注册等等。

即使您的引用仍然存在于下一个测试用例中,它们也会被您的设置方法覆盖并变为未引用,因此有资格进行垃圾回收。

如果JUnit为每个方法创建了一个新的测试用例实例(似乎就是这种情况),那么这些测试对象就不会被保留。 根据一项快速实验,至少没有测试通过。 因此,无论如何,它的大部分将被闲暇收集。