Android单元测试类的"private static final"成员将值更改为null

Jul*_*les 9 java junit android

我在Android项目中有一个JUnit测试用例,其中包含如下所示的代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

此测试类具有多个测试方法,其中一些参考(但不尝试修改)此常量的值.但是,当我运行这些测试(Android 2.2.2)时,所有这些测试但第一个测试失败了,logcat向我展示了这个:

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
Run Code Online (Sandbox Code Playgroud)

静态最终字段如何改变这样的值?我该如何防止这种情况发生?还有其他可能发生的情况吗?

---编辑1

我现在已经将代码修改为一个可以包含在其中的较小示例.见下文:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当此测试运行时,testA通过但testB失败.如果testA被注释掉,testB就会通过.

它是ServiceTestCase似乎很重要.标准的JUnit TestCase不会导致问题.如果'CONST2'是一个String,则两个测试都按预期传递.任何其他参考类型似乎重现了这个问题.

Dav*_*ess 7

似乎在每次测试后AndroidTestCase使用反射将所有非原始字段设置为.它不会检查字段是静态的还是最终的,因此这似乎是问题的根源.nullscrubClass

要解决此问题,请将字段更改为非最终字段并将其设置为内部setUp.此外,请确保将其super.setUp()作为第一行调用,setUp以确保测试用例已正确初始化.

  • 事实证明,这已经是Android中一个已知的错误近3年了.http://code.google.com/p/android/issues/detail?id=4244 (6认同)
  • 至于测试为什么传递常量为String,这是因为String被认为是编译时常量,并且对字段的引用被优化并替换为对String本身的直接引用.如果通过反射访问,我怀疑我会看到字段值改变,但由于没有使用它,这在这种情况下无关紧要. (3认同)
  • 我只是在常规的JVM中尝试过,而`setAccessible`没有效果; 即我无法覆盖最终价值.再看一下`scrubClass`,我不确定它是做什么的.文档似乎与实现不匹配,但我不确切地知道它是如何调用的.看起来它只是将那些本身就是测试用例的字段归零,这似乎很奇怪.除非你真的很想进一步挖掘,否则我会使用解决方法让它继续下去.:) (2认同)
  • 有趣的是,尽管它在Sun的JVM上不起作用,但Java语言规范似乎表明应该可以使用反射来修改最终字段.请参阅:http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3 (2认同)