如果测试跨越多个活动,是否有办法获得当前活动?
getActivity()方法只提供一个用于启动测试的活动。
我试过下面的东西,
public Activity getCurrentActivity() {
Activity activity = null;
ActivityManager am = (ActivityManager) this.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
try {
Class<?> myClass = taskInfo.get(0).topActivity.getClass();
activity = (Activity) myClass.newInstance();
}
catch (Exception e) {
}
return activity;
}
但我得到了空对象。
在Espresso中,您可以使用ActivityLifecycleMonitorRegistry
但它不受官方支持,因此在将来的版本中可能无法使用。
下面是它的工作原理:
Activity getCurrentActivity() throws Throwable {
getInstrumentation().waitForIdleSync();
final Activity[] activity = new Activity[1];
runTestOnUiThread(new Runnable() {
@Override
public void run() {
java.util.Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
activity[0] = Iterables.getOnlyElement(activities);
}});
return activity[0];
}
的build.gradle
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2',
{
exclude group: 'com.android.support', module: 'support-annotations'
})
并且,在您的仪器化测试课程中
@Rule
public ActivityTestRule<MainActivity> intentsTestRule = new ActivityTestRule<>(MainActivity.class);
MainActivity mainActivity;
@Before
public void setUp() throws Exception {
mainActivity = intentsTestRule.getActivity(); //now Activity's view gets created
//Because Activity's view creation happens in UI Thread, so all the test cases, here, are used by UI Thread
}
接受的答案可能不适用于许多浓咖啡测试。以下适用于在API 25设备上运行的espresso版本2.2.2和Android编译/目标SDK 27:
@Nullable
private Activity getActivity() {
Activity currentActivity = null;
Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED);
if (resumedActivities.iterator().hasNext()){
currentActivity = (Activity) resumedActivities.iterator().next();
}
return currentActivity;
}
如果您只需要对当前的Activity
进行检查,请使用可能与原生Espresso单行相关以检查是否已启动预期意图:
intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
如果不匹配,Espresso也会向您显示同时触发的意图。
您需要的唯一设置是在测试中用ActivityTestRule
替换IntentsTestRule
,让它跟踪启动的意图。并确保此库位于您的build.gradle
依赖项中:
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
我喜欢@ Ryan的版本,因为它不使用无证书的内部,但你可以写得更短:
private Activity getCurrentActivity() {
final Activity[] activity = new Activity[1];
onView(isRoot()).check(new ViewAssertion() {
@Override
public void check(View view, NoMatchingViewException noViewFoundException) {
activity[0] = (Activity) view.getContext();
}
});
return activity[0];
}
请注意,虽然在Firebase测试实验室中运行测试时这不起作用。那失败了
java.lang.ClassCastException: com.android.internal.policy.DecorContext cannot be cast to android.app.Activity
Android团队用ActivityTestRule
取代了ActivityScenario。我们可以用activityTestRule.getActivity()
做ActivityTestRule
而不用ActivityScenario。以下是我从Activity
获取ActivityScenario的解决方案(灵感来自@Ryan和@Fabian解决方案)
@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.java)
...
private fun getActivity(): Activity? {
var activity: Activity? = null
activityRule.scenario.onActivity {
activity = it
}
return activity
}
我无法使用任何其他解决方案,所以我最终不得不这样做:
宣布你的ActivityTestRule
:
@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
new ActivityTestRule<>(MainActivity.class);
声明一个final
Activity数组来存储您的活动:
private final Activity[] currentActivity = new Activity[1];
添加辅助方法以注册应用程序上下文以获取生命周期更新:
private void monitorCurrentActivity() {
mainActivityTestRule.getActivity().getApplication()
.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) { }
@Override
public void onActivityStarted(final Activity activity) { }
@Override
public void onActivityResumed(final Activity activity) {
currentActivity[0] = activity;
}
@Override
public void onActivityPaused(final Activity activity) { }
@Override
public void onActivityStopped(final Activity activity) { }
@Override
public void onActivitySaveInstanceState(final Activity activity, final Bundle outState) { }
@Override
public void onActivityDestroyed(final Activity activity) { }
});
}
添加辅助方法以获取当前活动
private Activity getCurrentActivity() {
return currentActivity[0];
}
所以,一旦你启动了你的第一个活动,只需要调用monitorCurrentActivity()
,然后当你需要引用当前活动时,你只需要调用getCurrentActivity()
public static Activity getActivity() {
final Activity[] currentActivity = new Activity[1];
Espresso.onView(AllOf.allOf(ViewMatchers.withId(android.R.id.content), isDisplayed())).perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(View.class);
}
@Override
public String getDescription() {
return "getting text from a TextView";
}
@Override
public void perform(UiController uiController, View view) {
if (view.getContext() instanceof Activity) {
Activity activity1 = ((Activity)view.getContext());
currentActivity[0] = activity1;
}
}
});
return currentActivity[0];
}
如果您的测试用例中只有Activity,则可以执行以下操作:
Rule
@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);
Activity
:mActivityTestRule.getActivity()
那是一块馅饼!
@lacton提出的解决方案对我不起作用,可能是因为活动不在ActivityLifecycleMonitorRegistry
报告的状态。
我甚至尝试过Stage.PRE_ON_CREATE
仍然没有得到任何活动。
注意:我无法使用ActivityTestRule
或IntentTestRule
,因为我使用activitiy-alias
开始我的活动,并且当我想测试以查看别名是否有效时,在测试中使用实际的类没有任何意义。
我的解决方案是通过ActivityLifecycleMonitorRegistry
订阅生命周期更改并阻止测试线程直到活动启动:
// NOTE: make sure this is a strong reference (move up as a class field) otherwise will be GCed and you will not stably receive updates.
ActivityLifecycleCallback lifeCycleCallback = new ActivityLifecycleCallback() {
@Override
public void onActivityLifecycleChanged(Activity activity, Stage stage) {
classHolder.setValue(((MyActivity) activity).getClass());
// release the test thread
lock.countDown();
}
};
// used to block the test thread until activity is launched
final CountDownLatch lock = new CountDownLatch(1);
final Holder<Class<? extends MyActivity>> classHolder = new Holder<>();
instrumentation.runOnMainSync(new Runnable() {
@Override
public void run() {
ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(lifeCycleCallback);
}
});
// start the Activity
intent.setClassName(context, MyApp.class.getPackage().getName() + ".MyActivityAlias");
context.startActivity(intent);
// wait for activity to start
lock.await();
// continue with the tests
assertTrue(classHolder.hasValue());
assertTrue(classHolder.getValue().isAssignableFrom(MyActivity.class));
Holder
基本上是一个包装器对象。您可以使用数组或其他任何内容来捕获匿名类中的值。
我改进了@Fabian Streitel答案,因此您可以在没有ClassCastException的情况下使用此方法
public static Activity getCurrentActivity() {
final Activity[] activity = new Activity[1];
onView(isRoot()).check((view, noViewFoundException) -> {
View checkedView = view;
while (checkedView instanceof ViewGroup && ((ViewGroup) checkedView).getChildCount() > 0) {
checkedView = ((ViewGroup) checkedView).getChildAt(0);
if (checkedView.getContext() instanceof Activity) {
activity[0] = (Activity) checkedView.getContext();
return;
}
}
});
return activity[0];
}