cdh*_*ker 21 junit android unit-testing android-context
更新2012年2月13日:接受了一个答案,解释说这种行为是一个错误,并指出它似乎已经比v 1.6更好地消失在模拟器上,这使得它对我们大多数人来说都不是问题.解决方法只是循环/休眠,直到getContext().getApplicationContext()返回非null.结束更新
按照android.app.Application的javadoc,我定义了一个单例(称为数据库)我所有的活动中访问了状态和持久性数据,并Database.getDatabase(上下文)通过Context.getApplicationContext获取应用程序上下文().当活动通过自己getDatabase(上下文)这个设置像宣传的那样,但是当我从AndroidTestCase的getApplicationContext(运行单元测试)经常呼叫返回null,虽然较长的测试中,更频繁地返回一个非空值.
以下代码在AndroidTestCase中重现null - 单例不是演示所必需的.
首先,要记录app-instantiation消息,在测试中的应用程序中我定义了MyApp并将其添加到清单中.
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Log.i("MYAPP", "this=" + this);
Log.i("MYAPP", "getAppCtx()=" + getApplicationContext());
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,我定义了一个测试用例,用于报告AndroidTestCase.getContext()4次,由一些睡眠和一个getSharedPreferences()调用分开:
public class DatabaseTest extends AndroidTestCase {
public void test_exploreContext() {
exploreContexts("XPLORE1");
getContext().getSharedPreferences("foo", Context.MODE_PRIVATE);
exploreContexts("XPLORE2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exploreContexts("XPLORE3");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exploreContexts("XPLORE4");
}
public void exploreContexts(String tag) {
Context testContext = getContext();
Log.i(tag, "testCtx=" + testContext +
" pkg=" + testContext.getApplicationInfo().packageName);
Log.i(tag, "testContext.getAppCtx()=" + testContext.getApplicationContext());
try {
Context appContext = testContext.createPackageContext("com.foo.android", 0);
ApplicationInfo appInfo = appContext.getApplicationInfo();
Log.i(tag, "appContext=" + appContext +
" pkg=" + appContext.getApplicationInfo().packageName);
Log.i(tag, "appContext.getAppCtx()=" + appContext.getApplicationContext());
} catch (NameNotFoundException e) {
Log.i(tag, "Can't get app context.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是生成的logCat的一大块(通过Eclipse在SDK11 WinXP上的1.6模拟器):
INFO/TestRunner(465): started: test_exploreContext(test.foo.android.DatabaseTest)
INFO/XPLORE1(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE1(465): testContext.getAppCtx()=null
INFO/XPLORE1(465): appContext=android.app.ApplicationContext@437801e8 pkg=com.foo.android
INFO/XPLORE1(465): appContext.getAppCtx()=null
INFO/XPLORE2(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE2(465): testContext.getAppCtx()=null
INFO/XPLORE2(465): appContext=android.app.ApplicationContext@43782820 pkg=com.foo.android
INFO/XPLORE2(465): appContext.getAppCtx()=null
INFO/MYAPP(465): this=com.foo.android.MyApplication@43783830
INFO/MYAPP(465): getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE3(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE3(465): testContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE3(465): appContext=android.app.ApplicationContext@43784768 pkg=com.foo.android
INFO/XPLORE3(465): appContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE4(465): testCtx=android.app.ApplicationContext@43757368 pkg=com.foo.android
INFO/XPLORE4(465): testContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/XPLORE4(465): appContext=android.app.ApplicationContext@43785778 pkg=com.foo.android
INFO/XPLORE4(465): appContext.getAppCtx()=com.foo.android.MyApplication@43783830
INFO/TestRunner(465): finished: test_exploreContext(test.foo.android.DatabaseTest)
Run Code Online (Sandbox Code Playgroud)
请注意,getApplicationContext()在一段时间内返回null,然后开始返回MyApp的实例.我一直没能得到在本次测试的不同运行同样的结果(这就是我结束了在4次迭代,睡觉,并getSharedPreferences是()调用尝试鹅应用到存在).
上面的LogCat消息块似乎最相关,但是单个测试的单个运行的整个LogCat很有趣.Android启动了4个AndroidRuntimes; 上面的块是从第4个.有趣的是,第3个运行时显示消息,指示它在进程ID 447中实例化了MyApp的不同实例:
INFO/TestRunner(447): started: test_exploreContext(test.foo.android.DatabaseTest)
INFO/MYAPP(447): this=com.foo.android.MyApplication@437809b0
INFO/MYAPP(447): getAppCtx()=com.foo.android.MyApplication@437809b0
INFO/TestRunner(447): finished: test_exploreContext(test.foo.android.DatabaseTest)
Run Code Online (Sandbox Code Playgroud)
我假设的TestRunner(447)消息来自其在过程465还是孩子家长测试线程报告,问题是:为什么Android的昏天暗地AndroidTestCase运行之前其上下文正确挂接到一个应用程序实例?
解决方法:如果我getContext().getSharedPreferences("anyname", Context.MODE_PRIVATE).edit().clear().commit();先打电话,我的一个测试似乎大多数时候都会避免空值,所以我就是这样做的.
顺便说一句:如果答案是"这是一个Android错误,你为什么不把它归档呢?哎呀,你为什么不修复它?" 然后我愿意做两件事.我还没有采取过成为bug-filer或贡献者的步骤 - 也许这是个好时机.
hac*_*bod 12
Instrumentation在主应用程序线程的单独线程中运行,因此它可以在不阻塞或中断(或被主线程阻塞)的情况下执行.如果需要与主线程同步,请使用例如:Instrumentation.waitForIdleSync()
特别是,Application对象以及Activity之类的所有其他顶级类都由主线程初始化.您的检测线程正在初始化时运行.如果您触摸任何这些对象并且没有实现自己的线程安全措施,则应该在主线程上运行此类代码,例如via:Instrumentation.runOnMainSync(java.lang.Runnable)