Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'private static final' member of Android unit test class changes value to null

I have a JUnit test case in an Android project that contains code that looks like this:

private static final URI TEST_RESOURCE_URL = TasksService.TASKLIST_RESOURCELIST_URL.resolve("task/test.task");

public void setUp () {
    Log.i("Test", "TEST_RESOURCE_URL=" + TEST_RESOURCE_URL);
}

This test class has multiple test methods, some of which refer to (but do not attempt to modify) the value of this constant. However, when I run these tests (Android 2.2.2), all of these tests but the first one fails, and logcat shows me this:

03-03 18:56:41.791: I/Test(12008): TEST_RESOURCE_URL=http://apate.meridiandigital.net/tasks/task/test.task
03-03 18:56:42.101: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.131: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.151: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.281: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.311: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.341: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.361: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.391: I/Test(12008): TEST_RESOURCE_URL=null
03-03 18:56:42.391: I/Test(12008): TEST_RESOURCE_URL=null

How does a static final field change value like this? How do I prevent this from happening? Are there other situations where it might happen?

--- EDIT 1

I have now trimmed the code down to a smaller example that can be included in its entirety. See below:

public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}


public class StaticFinalTest extends ServiceTestCase<MyService> {
    public StaticFinalTest() {
        super(MyService.class);
    }

    public static final Object CONST2 = new Object();

    public void testA ()
    {
        assertNotNull (CONST2);
    }

    public void testB ()
    {
        assertNotNull (CONST2);
    }
}

When this test runs, testA passes but testB fails. If testA is commented out, testB passes.

It seems to be important that it is a ServiceTestCase. A standard JUnit TestCase does not cause the problem. If 'CONST2' is a String, both tests pass as expected. Any other reference type seems to reproduce the problem.

like image 770
Jules Avatar asked Mar 03 '12 19:03

Jules


2 Answers

It appears that AndroidTestCase uses reflection to set all non-primitive fields to null after each test in scrubClass. It doesn't check to see if the fields are static or final, so this seems to be the source of the problem.

To solve it, change the field to non-final and set it inside setUp. Also, make sure you call super.setUp() as the first line of your setUp to make sure the test case is properly initialized.

like image 195
David Harkness Avatar answered Oct 20 '22 01:10

David Harkness


Is it possible that the garbage collector gets rid of it because it cannot know you're still accessing the object using JUnit?

Maybe you should create a method which sets the value initially. Don't forget to annotate it with @Begin in order to assure the correct initialization of this variable.

Anyways in a test case, you shouldn't be assuming that previous operations succeeded. The environment you need in a JUnit task should be built up from zero each time.

regards

like image 20
Atmocreations Avatar answered Oct 19 '22 23:10

Atmocreations