Last active
June 21, 2017 18:25
-
-
Save drewhannay/7fa758847cad8a6dc26f0d1d3cb068ad to your computer and use it in GitHub Desktop.
Android Espresso Activity Clean Up
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (C) 2017 Drew Hannay | |
* <p> | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* <p> | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* <p> | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package android.support.test.rule; | |
import android.app.Activity; | |
import android.support.test.runner.MonitoringInstrumentationAccessor; | |
import android.util.Log; | |
import java.lang.reflect.Field; | |
/** | |
* Subclass of {@link ActivityTestRule} that cleanly handles finishing multiple activities. | |
* <p/> | |
* The official ActivityTestRule only calls finish() on the initial activity. However, this can cause problems if the | |
* test ends in a different activity than which it was started. In this implementation, we call finish() on all | |
* Activity classes that are started and wait until they actually finish before proceeding. | |
*/ | |
public class FinishingActivityTestRule<T extends Activity> extends ActivityTestRule<T> { | |
private static final String TAG = FinishingActivityTestRule.class.getSimpleName(); | |
public FinishingActivityTestRule(Class<T> activityClass) { | |
super(activityClass); | |
} | |
public FinishingActivityTestRule(Class<T> activityClass, boolean initialTouchMode) { | |
super(activityClass, initialTouchMode); | |
} | |
public FinishingActivityTestRule(Class<T> activityClass, boolean initialTouchMode, boolean launchActivity) { | |
super(activityClass, initialTouchMode, launchActivity); | |
} | |
@Override | |
void finishActivity() { | |
MonitoringInstrumentationAccessor.finishAllActivities(); | |
// purposefully don't call super since we've already finished all the activities | |
// instead, null out the mActivity field in the parent class using reflection | |
try { | |
Field activityField = ActivityTestRule.class.getDeclaredField("mActivity"); | |
activityField.setAccessible(true); | |
activityField.set(this, null); | |
} catch (NoSuchFieldException e) { | |
Log.e(TAG, "Unable to get field through reflection", e); | |
} catch (IllegalAccessException e) { | |
Log.e(TAG, "Unable to get access field through reflection", e); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (C) 2017 Drew Hannay | |
* <p> | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* <p> | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* <p> | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package android.support.test.rule; | |
import android.app.Activity; | |
import android.support.test.espresso.intent.Intents; | |
/** | |
* Subclass of {@link FinishingActivityTestRule} that can be used in place of | |
* {@link android.support.test.espresso.intent.rule.IntentsTestRule} and still get the bug fix provided by | |
* FinishingActivityTestRule | |
*/ | |
public class FinishingIntentsTestRule<T extends Activity> extends FinishingActivityTestRule<T> { | |
private boolean isLoaded; | |
public FinishingActivityTestRule(Class<T> activityClass) { | |
super(activityClass); | |
} | |
public FinishingActivityTestRule(Class<T> activityClass, boolean initialTouchMode) { | |
super(activityClass, initialTouchMode); | |
} | |
public FinishingActivityTestRule(Class<T> activityClass, boolean initialTouchMode, boolean launchActivity) { | |
super(activityClass, initialTouchMode, launchActivity); | |
} | |
protected void afterActivityLaunched() { | |
Intents.init(); | |
isLoaded = true; | |
super.afterActivityLaunched(); | |
} | |
protected void afterActivityFinished() { | |
super.afterActivityFinished(); | |
if (isLoaded) { | |
// release() will NPE if init() wasn't called | |
// (usually happens if a test failed before the activity was launched) | |
Intents.release(); | |
isLoaded = false; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (C) 2017 Drew Hannay | |
* <p> | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* <p> | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* <p> | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package android.support.test.runner; | |
import android.os.Handler; | |
import android.os.Looper; | |
import android.support.test.InstrumentationRegistry; | |
import android.util.Log; | |
/** | |
* Helper class for calling protected methods in MonitoringInstrumentation | |
*/ | |
public class MonitoringInstrumentationAccessor { | |
private static final String TAG = MonitoringInstrumentationAccessor.class.getSimpleName(); | |
private static final Handler HANDLER_FOR_MAIN_LOOPER = new Handler(Looper.getMainLooper()); | |
private MonitoringInstrumentationAccessor() {} | |
/** | |
* Synchronously finish all currently started activities | |
*/ | |
public static void finishAllActivities() { | |
MonitoringInstrumentation instrumentation = (MonitoringInstrumentation) InstrumentationRegistry.getInstrumentation(); | |
MonitoringInstrumentation.ActivityFinisher activityFinisher = instrumentation.new ActivityFinisher(); | |
HANDLER_FOR_MAIN_LOOPER.post(activityFinisher); | |
long startTime = System.currentTimeMillis(); | |
instrumentation.waitForActivitiesToComplete(); | |
long endTime = System.currentTimeMillis(); | |
Log.i(TAG, String.format("waitForActivitiesToComplete() took: %sms", endTime - startTime)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Your constructors in FinishingIntentsRule are wrongly named - they reference FinishingActivityTestRule. I also think you should call afterActivityFinished in FinishingActivityTestRule::finishActivity.
Can you do a write up on why this is necessary? It seems really hard to Google this problem and get relevant results.