在使用基於Instrumentation測試框架如robotium時,Test run failed: Instrumentation run failed due to 'Process crashed.'這個報錯估計大多數人都遇到過,Android的應用是以Linux進程的概念在運行的,而Instrumentation與被測應用運行在同一個進程中,當被測應用的進程在Instrumentation本身退出前被關閉了,則會拋出Test run failed: Instrumentation run failed due to 'Process crashed.'這樣的錯誤。
測試過程中引起這種情況的主要有兩大類:
一、被測工程或測試工程本身代碼運行異常導致
這類比較好辦,首先手動運行被測的應用,沒問題的話就可以排除了。然後看看運行時的錯誤日誌,檢查下測試工程的配置之類,一般就可以很快定位到。
二、被測應用的主Activity在調用onDestory()方法時有調用如android.os.Process.killProcess(android.os.Process.myPid())或System.exit(0)這種退出進程的方法
這種情況應該是大多數導致Test run failed: Instrumentation run failed due to 'Process crashed.'的原因
Android應用中基本都會有個主入口的Activity,即應用的啓動頁,先看看我們常見的“再按一次退出程序”的實現:
/**
* 返回鍵退出應用
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
if((System.currentTimeMillis()-exitTime) > 2000){
Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
finish();
System.exit(0);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
有些應用是按一次返回鍵就退出程序了,但道理都是一樣的,即應用的主Activity會去監聽KEYCODE_BACK事件,當我們點擊BACK鍵時,程序調用System.exit(0)或android.os.Process.killProcess(android.os.Process.myPid())退出程序。
如果你的應用是類似這種實現,那麼測試時一般就不會碰到Instrumentation run failed due to 'Process crashed.問題了。
如果你的應用是隻要當前Activity被銷燬,即在onDestory()方法中調用System.exit(0)的話,且你的測試用例在tearDown()時調用了solo.finishOpenedActivities()方法,且你的測試用例調起了主入口的Activity,由於finishOpenedActivities()會將所有已打開過的Activity都銷燬,那麼主入口Activity也會被銷燬,而Activity被銷燬時會調用onDestory()方法,那麼此種情況必然每次都會報這個錯。
這種情況下網上有的會說在tearDown()時別調用finishOpenedActivities()方法,這顯然是不可行的,因爲這會使多個測試用例執行時卡住不能往下執行,因爲基於Instrumentation的測試框架,生命週期爲setUp()——>以test開頭的測試方法——>tearDown()
setUp()時會把被測的Activity調起,並做一些初始化相關,然後執行測試方法,最後tearDown()清理數據等,爲了保障每個用例儘可能地可靠、穩定、具有原子性,這種框架的生命週期還是得遵守的。
解決辦法的話,只能是讓開發修改退出程序的方式,不要在主Activity的onDestory()時直接調用System.exit(0)。
其它:當連續執行幾百個測試用例時,有時正常、有時又會偶然性地出現Test run failed: Instrumentation run failed due to 'Process crashed.'問題
由上文可知被測應用的主Activity會監聽KEYCODE_BACK事件,當你的用例有調起過或測試過程中會經過主Activity時,那麼任何帶有KEYCODE_BACK的事件都有可能導致報這個錯。
在robotium中會發送KEYCODE_BACK的主要用以下兩個方法:
一個是goBack()方法:
/**
* Simulates pressing the hardware back key.
*/
public void goBack() {
sleeper.sleep();
try {
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
sleeper.sleep();
} catch (Throwable ignored) {}
}
對於偶然性地出現這個報錯,測試用例中應該不會有人沒事老調用goBack()方法,因此應該不會是由於這個引起的。
另一個就是每個用例都會用到的finishOpenedActivities()方法:
/**
* All activites that have been opened are finished.
*/
public void finishOpenedActivities(){
// Stops the activityStack listener
activitySyncTimer.cancel();
ArrayList<Activity> activitiesOpened = getAllOpenedActivities();
// Finish all opened activities
for (int i = activitiesOpened.size()-1; i >= 0; i--) {
sleeper.sleep(MINISLEEP);
finishActivity(activitiesOpened.get(i));
}
activitiesOpened = null;
sleeper.sleep(MINISLEEP);
// Finish the initial activity, pressing Back for good measure
finishActivity(getCurrentActivity(true, false));
setRegisterActivities(false);
this.activity = null;
sleeper.sleepMini();
try {
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
sleeper.sleep(MINISLEEP);
inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
} catch (Throwable ignored) {
// Guard against lack of INJECT_EVENT permission
}
clearActivityStack();
}
由上可知源碼的作者在把所有的已打開的Activity都關閉後,還調用了兩次inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
其實正常情況下前半段代碼就已經能夠把所有的已打開的Activity都關閉了,但出於某些特殊情況的考慮,可能在那個for循環中並沒能將已打開的Activity均關閉,所以作者還特意多調用了兩次KEYCODE_BACK,而這也就帶來了較大的出現Instrumentation run failed due to 'Process crashed.'的風險。
解決的話比如可以在inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK)前增加判斷當前的Activity是否是你應用的主Activity