如何防止ActivityUnitTestCase调用Application.onCreate?

时间:2011-02-11 13:29:59

标签: android unit-testing

我必须在这里遗漏一些东西。 ActivityUnitTestCase的JavaDoc表明此测试用例独立于系统测试一个Activity:

  

此类提供单个活动的独立测试。将使用与系统基础结构的最小连接来创建测试中的活动,并且可以注入许多Activity的依赖项的模拟或包装版本。

我假设包括没有实际启动应用程序。此外,它暴露了一个setApplication助手,可以用来注入模拟应用程序。

但是,任何ActivityUnitTestCase我开始启动(实际)应用程序并调用其onCreate方法。更确切地说,InstrumentationTestRunner似乎正在这样做,甚至在我有机会setApplication测试的setUp方法之前就这样做了!我甚至没有注意到这一点,因为它似乎发生在测试套件启动期间甚至没有达到Eclipse断点的地方,但写入onCreate中的日志显示它实际上被调用。 / p>

这完全超出我的范围。当Android的测试运行器实例化并执行实际应用程序时,为什么我要使用模拟应用程序对象?考虑到instrumentation runner在自己的线程中运行,并且在执行此操作时生成主应用程序线程,这甚至更成问题。这意味着正在执行的测试与被调用的Application.onCreate之间存在竞争条件。如果您在那里做任何可能影响测试的事情,例如写入共享首选项文件,然后你就完全搞砸了,因为你的测试会随机失败。

我是否遗漏了某些内容,或者这只是对测试框架的严重疏忽?

更新 这似乎也会影响ApplicationTestCase。在我的测试用例开始之前,我可以在我的应用程序类“onCreate中找到一个断点。我们在那里启动了一个即发即弃的AsyncTask,它将随机失败,因为我没有机会嘲笑它(记住,在我的测试用例中调用setUp之前)。这是我在onCreate这个模糊的调用中看到的堆栈跟踪:

Thread [<1> main] (Suspended (breakpoint at line 86 in QypeRadar))  
QypeRadar.onCreate() line: 86   
InstrumentationTestRunner(Instrumentation).callApplicationOnCreate(Application) line: 969   
ActivityThread.handleBindApplication(ActivityThread$AppBindData) line: 4244 
ActivityThread.access$3000(ActivityThread, ActivityThread$AppBindData) line: 125    
ActivityThread$H.handleMessage(Message) line: 2071  
ActivityThread$H(Handler).dispatchMessage(Message) line: 99 
Looper.loop() line: 123 
ActivityThread.main(String[]) line: 4627    
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
Method.invoke(Object, Object...) line: 521  
ZygoteInit$MethodAndArgsCaller.run() line: 868  
ZygoteInit.main(String[]) line: 626 
NativeStart.main(String[]) line: not available [native method]  

为什么测试运行员callApplicationOnCreate即使the docs明确说明:

  

在您的测试调用createApplication()之前,测试用例不会调用onCreate()。这使您有机会在onCreate()之前设置或调整任何其他框架或测试逻辑。

这是一个平淡无奇的谎言 - 它没有给我机会!

2 个答案:

答案 0 :(得分:3)

Roboguice有同样的问题。检查here

答案 1 :(得分:2)

我正在用匕首进行测试,所以可能这也是你的情况,因为你可能只想注入而不要调用Application.onCreate中的任何内容,所以这个对我来说很好(api17 +):< / p>

private Context mContext;
private Application mApplication;

@Override
protected void setUp() throws Exception {
    super.setUp();

    mContext = new ContextWrapper(getInstrumentation().getTargetContext()) {
        @Override
        public Context getApplicationContext() {
            return mApplication;
        }
    };
    mApplication = new MyAppMock();
    mApplication.attachBaseContext(mContext); 

    setApplication(app);
}

public void testActivityCreated() {
    Intent intent = AboutActivity.createIntent(mContext);
    setActivityContext(mContext);
    startActivity(intent, null, null);
    assertNotNull(getActivity());
}

对于&lt; api16你需要使用反射并调用Application.attach(context)代替Application.attachBaseContext()设置Application.mLoadedApk,否则会崩溃。

我已将所有内容放在一起并制作演示应用程序,演示如何使用匕首进行测试: https://github.com/vovkab/dagger-unit-test

它还展示了如何模拟你的应用程序,适用于任何Android版本。