Testing WeakReference

隐身守侯 提交于 2021-02-18 21:29:06

问题


What is the proper approach to testing a weak reference in Java?

My initial idea is to do the following:

public class WeakReferenceTest {

    public class Target{
        private String value;    

        public Target(String value){
            this.value = value;
        }    
        public String toString(){
            return value;
        }
    }

    public class UsesWeakReference{    
        WeakReference<Target> reference;   

        public UsesWeakReference(Target test){
            reference = new WeakReference<Target>(test);
        }    
        public String call(){
            Target test = reference.get();
            if(test != null){
                return test.toString();
            }
            return "empty";
        }
    }

    @Test
    public void testWeakReference(){    
        Target target = new Target("42");

        UsesWeakReference usesWeakReference = new UsesWeakReference(target);    
        WeakReference<Target> triggerReference = new WeakReference<Target>(target);    
        assertEquals("42", usesWeakReference.call());

        target = null;    
        while(triggerReference.get() != null){
            System.gc();
        }

        assertEquals("empty", usesWeakReference.call());    
    }    
}

The reservation I have about the approach is using System.gc(), as I understand that it can behave differently on different JVMs.


回答1:


There's no 100% bombproof way of testing code that uses the Reference types. The behaviour of Reference objects depends on when the GC runs, and there is no 100% reliable way of forcing the GC to run.

The best you can do is:

  • check that you have the right JVM options set when running the tests, and
  • write your test so that it doesn't fail in the event that System.gc() is a no-op OR be willing to disable or skip the test, or ignore the test failure.

(You should be able to detect that System.gc() is being ignored by looking at how much memory is in use before and after the call; e.g. by calling Runtime.totalMemory())


Actually, there is another "solution". Have your unit test generate a huge amount of garbage ... enough to guarantee that you will trigger garbage collection. (Not a good idea, IMO.)




回答2:


New answer to old question; I found your question as I am dealing with the exact same problem: I want to write a unit test in order to verify that my class under test does something very specific if the referent of a WeakReference turns null.

I first wrote a simple test case that would set the referent to null; to then call System.gc(); and interestingly enough: at least within my eclipse, that was "good enough" for my weakRefernce.get() to return null.

But who knows if that will work for all the future environments that will run this unit test for the years to come.

So, after thinking some more:

@Test
public void testDeregisterOnNullReferentWithMock() {
    @SuppressWarnings("unchecked")
    WeakReference<Object> weakReference = EasyMock.createStrictMock(WeakReference.class);
    EasyMock.expect(weakReference.get()).andReturn(null);
    EasyMock.replay(weakReference);
    assertThat(weakReference.get(), nullValue());
    EasyMock.verify(weakReference);
}

Works nicely, too.

Meaning: the generic answer to this problem is a factory that creates WeakReference for objects for you. So, when you want to test your production code; you provide a mocked factory to it; and that factory will in turn mocked WeakReference objects; and now you are in full control regarding the behavior of that weak reference object.

And "full control" is much better than assuming that the GC maybe does what you hope it is doing.




回答3:


I want to piggyback off of GhostCat salutes Monica C's answer which says to use mocks. This is certainly one route, however while implementing this I noticed there is actually a clear() function on WeakReference itself. So rather than doing the heavy lifting of creating the mocks you can create a factory instance and simply clear the referent yourself. The approach I used is written in Kotlin so hopefully the syntax isn't too jarring, but this is exactly what I have.

Our Factory looks like this, you can think of the invoke() function as a constructor, functionally that's what it is doing, practically it saves us from implementing a class for the default behavior.

interface WeakReferenceFactory {
    fun <T> create(referent: T): WeakReference<T>

    companion object {
        // Allows us to create a production ready instance with WeakReferenceFactory(), avoids having to implement a concrete instance.
        operator fun invoke(): WeakReferenceFactory {
            return object : WeakReferenceFactory {
                override fun <T> create(referent: T): WeakReference<T> {
                    return WeakReference(referent)
                }
            }
        }
    }
}

For our tests we can then implement the Factory with an additional clear() function, doing so allows us to keep a reference of the instance we are using in a test and then simply pass it into the factory to be cleared.

class WeakReferenceFactoryFake : WeakReferenceFactory {
    private val managedReferents = mutableListOf<WeakReference<*>>()

    fun <T> clear(referent: T) {
        managedReferents.filter { it.get() == referent }
            .forEach { it.clear() }
    }

    override fun <T> create(referent: T): WeakReference<T> {
        val weakReference = WeakReference(referent)
        managedReferents.add(weakReference)
        return weakReference
    }
}

Then within your test you would have something like this (sorry for the Foo's and Bar's).

class FooTest {
    private val fakeWeakReferenceFactory = WeakReferenceFactoryFake()

    private val subject: Foo = Foo(fakeWeakReferenceFactory)

    @Test
    fun `given foo, when bar is cleared, then bar should be null`() {
        val bar = Bar()
        foo.put(bar)

        fakeWeakReferenceFactory.clear(bar)

        assert(bar).isNull()
    }
}


来源:https://stackoverflow.com/questions/11174328/testing-weakreference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!