Espresso - How can I check if an activity is launched after performing a certain action?
AndroidAndroid ActivityAndroid EspressoAndroid Problem Overview
the following is one of my Espresso test cases.
public void testLoginAttempt() {
Espresso.onView(ViewMatchers.withId(R.id.username)).perform(ViewActions.clearText()).perform(ViewActions.typeText("[email protected]"));
Espresso.onView(ViewMatchers.withId(R.id.username)).perform(ViewActions.clearText()).perform(ViewActions.typeText("invalidpassword"));
Espresso.onView(ViewMatchers.withId(R.id.login_button)).perform(ViewActions.click());
// AFTER CLICKING THE BUTTON, A NEW ACTIVITY WILL POP UP.
// Clicking launches a new activity that shows the text entered above. You don't need to do
// anything special to handle the activity transitions. Espresso takes care of waiting for the
// new activity to be resumed and its view hierarchy to be laid out.
Espresso.onView(ViewMatchers.withId(R.id.action_logout))
.check(ViewAssertions.matches(not(ViewMatchers.isDisplayed())));
}
Currently what I did was to check if a view in the new activity (R.id.action_logout) is visibible or not. If visible, I will assume tha the activity opened successfully. But it doesn't seem to work as I expected. Is there a better way to check if a new activity is successfully launched instead of checking a view in that activity is visible? Thanks
Android Solutions
Solution 1 - Android
You can use:
intended(hasComponent(YourExpectedActivity.class.getName()));
Requires this gradle entry:
androidTestCompile ("com.android.support.test.espresso:espresso-intents:$espressoVersion")
The import for the intended()
and hasComponent()
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
as mentioned by Shubam Gupta please remember to call Intents.init()
before calling intended()
. You can eventually call it in the @Before
method.
Solution 2 - Android
Try:
intended(hasComponent(YourActivity.class.getName()));
Also, keep in mind
java.lang.NullPointerException
is thrown if Intents.init()
is not called before intended()
Solution 3 - Android
You may do it as follows:
@Test
public void testLoginAttempt() {
Espresso.onView(ViewMatchers.withId(R.id.username)).perform(ViewActions.clearText()).perform(ViewActions.typeText("[email protected]"));
Espresso.onView(ViewMatchers.withId(R.id.username)).perform(ViewActions.clearText()).perform(ViewActions.typeText("invalidpassword"));
Intents.init();
Espresso.onView(ViewMatchers.withId(R.id.login_button)).perform(ViewActions.click());
Intents.release();
}
java.lang.NullPointerException
is thrown if Intents.init()
is not called.
Solution 4 - Android
Make sure the Espresso intent library is in the gradle dependencies
androidTestImplementation "com.android.support.test.espresso:espresso-intents:3.0.1"
Then import these two in your test file
import static android.support.test.espresso.intent.Intents.intended
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
Then add IntentsTestRule in your test class
@Rule
@JvmField
val mainActivityRule = IntentsTestRule(MainActivity::class.java)
Finally check the activity has launched intent
@Test
fun launchActivityTest() {
onView(ViewMatchers.withId(R.id.nav_wonderful_activity))
.perform(click())
intended(hasComponent(WonderfulActivity::class.java!!.getName()))
}
Solution 5 - Android
The problem is that your application performs the network operation after you click login button. Espresso doesn't handle (wait) network calls to finish by default. You have to implement your custom IdlingResource which will block the Espresso from proceeding with tests until IdlingResource returns back in Idle state, which means that network request is finished. Take a look at Espresso samples page - https://google.github.io/android-testing-support-library/samples/index.html
Solution 6 - Android
Try with
intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
Look at response from @riwnodennyk
Solution 7 - Android
I use this approach:
// The IntentsTestRule class initializes Espresso Intents before each test, terminates the host activity, and releases Espresso Intents after each test
@get:Rule
var tradersActivity: IntentsTestRule<TradersActivity> = IntentsTestRule(TradersActivity::class.java)
@get:Rule
var jsonViewActivity: IntentsTestRule<JsonViewActivity> = IntentsTestRule(JsonViewActivity::class.java)
@Test
fun scrollToItemAndClick() {
onView(withId(R.id.tradersRecyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(ITEM_POS, click()))
// check is activity was started
intended(hasComponent(JsonViewActivity::class.java.getName()))
}
Solution 8 - Android
@RunWith(RobolectricTestRunner.class)
public class WelcomeActivityTest {
@Test
public void clickingLogin_shouldStartLoginActivity() {
WelcomeActivity activity = Robolectric.setupActivity(WelcomeActivity.class);
activity.findViewById(R.id.login).performClick();
Intent expectedIntent = new Intent(activity, LoginActivity.class);
assertThat(shadowOf(activity).getNextStartedActivity()).isEqualTo(expectedIntent);
}
}