在活动单元测试中,我如何模拟活动生命周期事件。
我可以在现有活动上调用instrumentation的callActivityOn ...方法,但是如何触发活动重新创建,以便活动的OnCreate获取具有已保存状态的包
答案 0 :(得分:9)
我发现此代码会导致创建新的Activity:
myActivity.finish();
setActivity(null);
myActivity = getActivity();
但这不会导致调用onSaveInstanceState。因此,例如,为了测试在视图方向改变之后是否正确创建了活动,这样的测试应该这样做:
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);
}
});
答案 1 :(得分:6)
请勿关注state management test example:{dead link}
myActivity.finish();
myActivity = getActivity();
ActivityInstrumentationTestCase2.getActivity()
在您第一次调用它时启动Activity,但它只是在测试用例的每个后续调用中返回相同的Activity 。因此,您仍在查看已完成的活动。
完成第一个活动后,您需要从测试中开始一个新活动。例如,您可以使用InstrumentationTestCase.launchActivity()
。
作为另一个例子,我编写了一个测试,在ActivityA中按下一个按钮,启动ActivityB for-result;测试然后立即杀死ActivityA(通过方向更改,但finish()也可以工作),然后测试获取系统在ActivityB完成时创建并发送其结果的新ActivityA的句柄。有一个技巧是让测试添加一个Instrumentation.ActivityMonitor,然后让该监视器等待系统启动新的ActivityA并为测试提供一个句柄。
EDIT 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();
}
}
测试用例:( 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,甚至不会重新创建原始的Activity。
答案 2 :(得分:3)
我确认cdhabecker
是正确的,getActivity()会返回在开头创建的活动,即使您“完成”它也是如此。但我想我已经找到了一种测试活动娱乐的解决方案。您可以尝试请求更改方向。这将重新创建您的活动,然后您检索新创建的。下面的代码段:(我使用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);
}
当然,如果您在方向更改后阻止您的活动被销毁,它将无效。 Here您可以找到包含日志消息的完整答案。希望有所帮助。问候!
答案 3 :(得分:1)
如果您使用的是Android 4.x设备,则可以进入设置&gt;开发人员选项和检查'不要保持活动'。现在,只要你的Activity失去焦点(例如:HOME按钮),它就会被杀死并且将调用onSaveInstanceState(...)。
当您恢复应用时,如果您将其保存在onSaveInstanceState(...)中,则您的活动应该在onCreate(...)方法中包含数据包。
答案 4 :(得分:1)
官方开发指南中有一个非常好的例子,谈论州管理测试here。基本上你只需要调用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());
}
希望这有帮助。
答案 5 :(得分:1)
在阐述cdhabecker的答案时,我创建了以下适用于我的静态方法:
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类中。
答案 6 :(得分:0)
您可以通过ActivityLifeCycleMonitor
例如,此方法等待并将新创建的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。
你可以像这样在旋转上测试Activity娱乐:
Activity activity = getActivity(); // old activity
//rotate it
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//set new Activity
waitAndSetResumedActivity();
activity = getActivity(); // New Activity