Espresso does no record any intent if there are no buttons
The solution is to register an idling resource to wait the second activity.
In my case the test will remain as follows:
@Test
public void shoulddosomething() {
startActivity();
String templatePictureActivityClassName = TemplatePictureCaptureActivity.class.getName();
Espresso.registerIdlingResources(new WaitActivityIsResumedIdlingResource(templatePictureActivityClassName));
intended(hasComponent(hasClassName(templatePictureActivityClassName)));
}
And here the idling resource.
private static class WaitActivityIsResumedIdlingResource implements IdlingResource {
private final ActivityLifecycleMonitor instance;
private final String activityToWaitClassName;
private volatile ResourceCallback resourceCallback;
boolean resumed = false;
public WaitActivityIsResumedIdlingResource(String activityToWaitClassName) {
instance = ActivityLifecycleMonitorRegistry.getInstance();
this.activityToWaitClassName = activityToWaitClassName;
}
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public boolean isIdleNow() {
resumed = isActivityLaunched();
if(resumed && resourceCallback != null) {
resourceCallback.onTransitionToIdle();
}
return resumed;
}
private boolean isActivityLaunched() {
Collection<Activity> activitiesInStage = instance.getActivitiesInStage(Stage.RESUMED);
for (Activity activity : activitiesInStage) {
if(activity.getClass().getName().equals(activityToWaitClassName)){
return true;
}
}
return false;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
}
This might be related to a race condition on the Intents component initialization and not with a race condition between the startActivity
call and the usage of intended
. If you start your activity from the SUT activity onCreate
or onResume
methods you should take a look at the following test rule.
I've created an IntentsTestRule fixing this issue. https://gist.github.com/pedrovgs/6a305ba4c5e3acfac854ce4c36558d9b
package com.aplazame.utils
import android.app.Activity
import androidx.test.espresso.intent.Intents
import androidx.test.rule.ActivityTestRule
class ExhaustiveIntentsTestRule<T : Activity> : ActivityTestRule<T> {
private var isInitialized: Boolean = false
constructor(activityClass: Class<T>) : super(activityClass)
constructor(activityClass: Class<T>, initialTouchMode: Boolean) : super(activityClass, initialTouchMode)
constructor(activityClass: Class<T>, initialTouchMode: Boolean, launchActivity: Boolean) : super(
activityClass,
initialTouchMode,
launchActivity
)
override fun beforeActivityLaunched() {
super.beforeActivityLaunched()
Intents.init()
isInitialized = true
}
override fun afterActivityFinished() {
super.afterActivityFinished()
if (isInitialized) {
// Otherwise will throw a NPE if Intents.init() wasn't called.
Intents.release()
isInitialized = false
}
}
}
The main difference with the original IntentsTestRule implemented in AndroidX is the Intents.init() initialization. This time is invoked before starting the SUT activity. Keep in mind that this test rule will also record the intent used to start the SUT activity.