活动生命周期unit testing

在活动unit testing中,如何模拟活动生命周期事件。

我可以在现有活动上调用instrumentation的callActivityOn …方法,但是如何触发活动重新创build,以便活动的OnCreate获取已保存状态的包

Solutions Collecting From Web of "活动生命周期unit testing"

我发现这个代码会导致创build新的Activity:

myActivity.finish(); setActivity(null); myActivity = getActivity(); 

但是这不会导致onSaveInstanceState被调用。 因此,例如为了testing活动是否在视图方向改变之后被适当地创build,这样的testing应该这样做:

 private mInstrumentation = getInstrumentation(); ... final Bundle outState = new Bundle(); mInstrumentation.callActivityOnSaveInstanceState(mActivity, outState); mActivity.finish(); setActivity(null); mActivity = getActivity(); runTestOnUiThread(new Thread() { @Override public void run() { mInstrumentation.callActivityOnRestoreInstanceState(mActivity, outState); } }); 

不要按照州pipe理testing的例子 :{dead link}

 myActivity.finish(); myActivity = getActivity(); 

ActivityInstrumentationTestCase2.getActivity()在您第一次调用Activity时启动Activity,但是它只是在testing用例的每个后续调用中返回相同的Activity 。 因此,你仍然在看你完成的活动。

完成第一个活动后,您需要从testing中启动一个新活动。 例如,您可以使用InstrumentationTestCase.launchActivity()

作为另一个例子,我写了一个testing,在ActivityA中按下一个button,启动ActivityB for-result; 然后testing立即杀死ActivityA(通过方向更改,但finish()也可以),然后testing得到ActivityB完成时系统创build并发送结果的新ActivityA的句柄。 诀窍就是让testing添加一个Instrumentation.ActivityMonitor,然后让该监视器等待系统启动新的ActivityA并给testing一个句柄。

编辑2/23/2012 cdhabecker,添加可重复的代码:

 public class VerboseActivity extends Activity { public final static String TAG = "Verbose"; @Override protected void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate() " + (Activity)this); super.onCreate(savedInstanceState); setContentView(R.layout.activity5); } @Override protected void onDestroy() { Log.i(TAG, "onDestroy()."); super.onDestroy(); } } 

testing用例:(sleep()调用给活动很多时间来响应)

 public class VerboseTest extends ActivityInstrumentationTestCase2<VerboseActivity> { Activity myActivity = null; public VerboseTest() { super("com.scanillion.demo", VerboseActivity.class); } public void test_01() { String TAG = "test_01"; myActivity = getActivity(); Log.i(TAG, "A getActivity()=" + myActivity); myActivity.finish(); try { Thread.sleep(5000L); } catch (InterruptedException e) { } myActivity = getActivity(); Log.i(TAG, "B getActivity()=" + myActivity); try { Thread.sleep(5000L); } catch (InterruptedException e) { } } } 

日志:

 02-23 21:25:37.689: I/Verbose(17747): onCreate() com.scanillion.demo.VerboseActivity@43ba3360 02-23 21:25:38.159: I/ActivityManager(67): Displayed activity com.scanillion.demo/.VerboseActivity: 526 ms (total 526 ms) 02-23 21:25:38.180: I/test_01(17747): A getActivity()=com.scanillion.demo.VerboseActivity@43ba3360 02-23 21:25:38.540: I/Verbose(17747): onDestroy(). 02-23 21:25:43.236: I/test_01(17747): B getActivity()=com.scanillion.demo.VerboseActivity@43ba3360 02-23 21:25:48.439: I/TestRunner(17747): finished: test_01(com.scanillion.demo.test.VerboseTest) 02-23 21:25:48.439: I/TestRunner(17747): passed: test_01(com.scanillion.demo.test.VerboseTest) 

请注意, finish()导致onDestroy() ,但随后的getActivity()是无操作的。 getActivity()不是实例化一个新的Activity,它甚至不重新创build一个。

我确认cdhabecker是正确的,getActivity()返回在开始时创build的活动,即使你“完成”它。 但我想我已经find了一个解决scheme来testing娱乐的活动。 您可以尝试请求方向更改。 这将重新创build您的活动,然后您检索新创build。 下面的代码片段:(我使用robotium ):

 protected void setUp() throws Exception { super.setUp(); mActivity = getActivity(); mSolo = new Solo(getInstrumentation(), getActivity()); Log.v(TAG, "setUp; activity=" + mActivity); } public void testOrienationChange(){ mSolo.setActivityOrientation(Solo.LANDSCAPE); getInstrumentation().waitForIdleSync(); MyActivity newActivity = getActivity(); //should be new, but it's not Activity newActivity2 = mSolo.getCurrentActivity(); //this will return newly created Log.v(TAG, "testOrienationChange; activity=" + newActivity); Log.v(TAG, "testOrienationChange; activity2=" + newActivity2); } 

当然,如果您在定位更改之后阻止您的活动被销毁,它将无法工作。 在这里你可以find我的完整答案,包括日志消息。 希望有所帮助。 问候!

如果您有Android 4.x设备,则可以进入设置>开发人员选项,然后选中“不要保留活动”。 现在每当你的活动失去焦点(例如:HOMEbutton),它将被杀死,onSaveInstanceState(…)将被调用。

当您恢复应用程序时,如果您将Activity保存在onSaveInstanceState(…)中,则Activity应该在onCreate(…)方法中包含数据。

官方开发指南中有一个很好的例子,在这里谈论国家pipe理testing。 基本上你只需要调用Activity.finish()来模拟已经被杀死的活动,查看下面的伪代码:

 public void testIfStateIsSaved() { // Open myActivity first time. MyActivity myActivity = getActivity(); final EditText editText = (EditText) myActivity.findViewById(com.company.R.id.edit_text); // emulate some user action myActivity.runOnUiThread(new Runnable() { public void run() { editText.setText("save me"); } }); // Suppose you have implemented saved state properly. // kill activity and restart it again. myActivity.finish(); myActivity = getActivity(); final EditText editText2 = (EditText) myActivity.findViewById(com.company.R.id.edit_text); assertEquals("user input must be saved", "save me", editText2.getText()); } 

希望这可以帮助。

在cdhabecker的答案详细阐述,我创build了以下静态方法适用于我:

 public static Activity restartActivity(Activity activity, Instrumentation instrumentation, Intent intent){ String className = activity.getClass().getName(); Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(className, null, false); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName(instrumentation.getTargetContext(), className ); instrumentation.startActivitySync(intent); Activity newActivity = instrumentation.waitForMonitor(monitor); instrumentation.removeMonitor(monitor); return newActivity; } 

使用该活动后,我将其销毁并通过调用重置

 activity.finish(); setActivity(null); 

在我的ActivityInstrumentationTestCase2类中。

您可以通过ActivityLifeCycleMonitor获取新的恢复ActivityLifeCycleMonitor

例如,此方法将等待并将新创build的“ Activity为当前“活动”。

 public void waitAndSetResumedActivity() { // well at least there are some activities in the pipeline - lets see if they resume. long[] waitTimes = {10, 50, 100, 500, TimeUnit.SECONDS.toMillis(2), TimeUnit.SECONDS.toMillis(30)}; final ActivityLifecycleMonitor activityLifecycleMonitor = ActivityLifecycleMonitorRegistry.getInstance(); final AtomicBoolean activityResumed = new AtomicBoolean(false); for (int waitIdx = 0; waitIdx < waitTimes.length; waitIdx++) { if (activityResumed.get()) return; try { Thread.sleep(waitTimes[waitIdx]); } catch (InterruptedException e) { e.printStackTrace(); } getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { Collection<Activity> resumedActivities = activityLifecycleMonitor.getActivitiesInStage(Stage.RESUMED); if (!resumedActivities.isEmpty()) { activity = (MainActivity) resumedActivities.iterator().next(); setActivity(activity); activityResumed.set(true); } } }); } throw new NoActivityResumedException("No activities in stage RESUMED. Did you forget to " + "launch the activity. (test.getActivity() or similar)?"); } 

所以在调用这个方法之后,任何对getActivity()调用都会返回新的Activity。

你可以像这样testing旋转的Activity活动:

 Activity activity = getActivity(); // old activity //rotate it activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //set new Activity waitAndSetResumedActivity(); activity = getActivity(); // New Activity