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

emo*_*ory 14 java junit

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

JUnit为4个测试中的每个测试创建一个新的测试类实例.每个实例都包含一个Object obj.此obj也作为静态WeakHashMap的键插入.如果JUnit释放对测试实例的引用,则关联的obj值将被弱引用,因此符合gc条件.测试试图强制gc.WeakHashMap的大小将告诉我objs是否是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 < Object , Object > map = new WeakHashMap < Object , Object > ( ) ;

    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 ( "<BEFORE TEST " + id + ">" ) ;
    }

    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 ( "<AFTER TEST " + id + ">" ) ;
    }

    /**
     * Try to force a gc when one is not really needed.
     **/
    void gc ( )
    {
    ArrayList < Object > waste = new ArrayList < Object > ( ) ;
    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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

.<BEFORE TEST 0>
BEFORE=1    AFTER=1
Nullifying obj
<AFTER TEST 0>
.<BEFORE TEST 1>
BEFORE=2    AFTER=1
<AFTER TEST 1>
.<BEFORE TEST 2>
BEFORE=2    AFTER=1
Nullifying obj
<AFTER TEST 2>
.<BEFORE TEST 3>
BEFORE=2    AFTER=1
<AFTER TEST 3>
Run Code Online (Sandbox Code Playgroud)

Test0 obj无效,在Test1中它是gc'ed.但Test1 obj没有消失,它在Test2中得到了gc'ed.这表明无需取消对象.

Nam*_*ter 23

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

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

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

换句话说,对于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在推倒(做,当然,免费的资源).如果您正在编写JUnit3样式的测试,在其中分配大对象setUp()并将这些对象存储在其中TestCase,请考虑将字段设置为null.