Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android UI Testing

Android UI Testing

Barista is an awesome library that helps you write simple to understand and easy to read UI tests. The library also contains helpful Rules you can use to setup your tests properly which will increase the stability of your tests. This is an intro to the Barista library and what it offers

Avatar for Peter Törnhult

Peter Törnhult

February 10, 2020

More Decks by Peter Törnhult

Other Decks in Technology

Transcript

  1. @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { @Rule @JvmField var mActivityTestRule =

    ActivityTestRule(MainActivity::class.java) @Rule @JvmField var mGrantPermissionRule = GrantPermissionRule.grant( "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION") @Test fun mainActivityTest() { val appCompatButton = onView( allOf(withId(android.R.id.button1), withText("Accept"), childAtPosition( childAtPosition( withId(R.id.buttonPanel), 0), 3))) appCompatButton.perform(scrollTo(), click()) val bottomNavigationItemView = onView( allOf(withId(R.id.action_games), withContentDescription("Games"), childAtPosition( childAtPosition( withId(R.id.tabs), 0), 1), isDisplayed())) bottomNavigationItemView.perform(click()) val floatingActionButton = onView( allOf(withId(R.id.createNewGameButton),
  2. @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { @Rule @JvmField var mActivityTestRule =

    ActivityTestRule(MainActivity::class.java) @Rule @JvmField var mGrantPermissionRule = GrantPermissionRule.grant( "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION") @Test fun mainActivityTest() { val appCompatButton = onView( allOf(withId(android.R.id.button1), withText("Accept"), childAtPosition( childAtPosition( withId(R.id.buttonPanel), 0), 3))) appCompatButton.perform(scrollTo(), click()) val bottomNavigationItemView = onView( allOf(withId(R.id.action_games), withContentDescription("Games"), childAtPosition( childAtPosition( withId(R.id.tabs), 0), 1), isDisplayed())) bottomNavigationItemView.perform(click()) val floatingActionButton = onView( allOf(withId(R.id.createNewGameButton), childAtPosition( allOf(withId(R.id.games_container), childAtPosition( withClassName(`is`("android.widget.FrameLayout")), 0)), 2), isDisplayed())) floatingActionButton.perform(click()) val materialCardView = onView( allOf(childAtPosition( allOf(withId(R.id.list), childAtPosition( withId(R.id.swipeRefreshListLayout), 0)), 2), isDisplayed())) materialCardView.perform(click()) val materialCardView2 = onView( allOf(childAtPosition( allOf(withId(R.id.list), childAtPosition( withId(R.id.swipeRefreshListLayout), 0)), 2), isDisplayed())) materialCardView2.perform(click()) val materialCardView3 = onView( allOf(childAtPosition( allOf(withId(R.id.list), childAtPosition( withId(R.id.swipeRefreshListLayout), 0)), 4), isDisplayed())) materialCardView3.perform(click()) val appCompatButton2 = onView( allOf(withId(R.id.gameType), withText("Stroke Play"), childAtPosition( allOf(withId(R.id.bottomMenu), childAtPosition( withClassName(`is`("androidx.coordinatorlayout.widget.CoordinatorLayout")), 2)), 0), isDisplayed())) appCompatButton2.perform(click()) val appCompatRadioButton = onView( allOf(withId(R.id.matchPlay), withText("Match play\nTwo players"), childAtPosition( childAtPosition( withClassName(`is`("android.widget.ScrollView")), 0), 2))) appCompatRadioButton.perform(scrollTo(), click()) } private fun childAtPosition( parentMatcher: Matcher<View>, position: Int): Matcher<View> { return object : TypeSafeMatcher<View>() { override fun describeTo(description: Description) { description.appendText("Child at position $position in parent ") parentMatcher.describeTo(description) } public override fun matchesSafely(view: View): Boolean { val parent = view.parent return parent is ViewGroup && parentMatcher.matches(parent) && view == parent.getChildAt(position) } } } }
  3. @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { @Rule @JvmField var mActivityTestRule =

    ActivityTestRule(MainActivity::class.java) @Rule @JvmField var mGrantPermissionRule = GrantPermissionRule.grant( "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION") ... }
  4. private fun childAtPosition( parentMatcher: Matcher<View>, position: Int): Matcher<View> { return

    object : TypeSafeMatcher<View>() { override fun describeTo(description: Description) { description.appendText("Child at position $position in parent ") parentMatcher.describeTo(description) } public override fun matchesSafely(view: View): Boolean { val parent = view.parent return parent is ViewGroup && parentMatcher.matches(parent) && view == parent.getChildAt(position) } } }
  5. Espresso: Activity Rule @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { @Rule @JvmField

    var mActivityTestRule = ActivityTestRule(MainActivity::class.java) }
  6. Espresso: Test(s) @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { @Rule @JvmField var

    mActivityTestRule = ActivityTestRule(MainActivity::class.java) @Test fun app_should_launch() { ... } @Test fun feature1_should_be_shown() { ... } }
  7. Espresso: Grant Permission Rule @LargeTest @RunWith(AndroidJUnit4::class) class MainActivityTest { @Rule

    @JvmField var mActivityTestRule = ActivityTestRule(MainActivity::class.java) @Rule @JvmField var mGrantPermissionRule = GrantPermissionRule.grant( "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION") … }
  8. Espresso: Import members @Test fun mainActivityTest() { Espresso.onView(ViewMatchers.withText("Accept")) .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) .perform(ViewActions.scrollTo(),

    ViewActions.click()) Espresso.onView(ViewMatchers.withId(R.id.nav_games)) .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) .perform(ViewActions.click()) … }
  9. Espresso: Duplicate ViewMatch + ViewCheck ?!? @Test fun mainActivityTest() {

    onView(allOf(withText("Accept"), isDisplayed())) .check(matches(isDisplayed())) .perform(scrollTo(), click()) onView(allOf(withId(R.id.nav_games), isDisplayed())) .check(matches(isDisplayed())) .perform(click()) … }
  10. Barista: Assertions assertDisplayed("Ok") assertDisplayed(R.string.ok) assertDisplayed(R.id.ok_button) assertDisplayed(R.id.ok_button, R.string.ok) assertEnabled(...) / assertDisabled(...)

    assertNotExist(...) assertChecked(...) / assertUnchecked(...) assertClickable(...) / assertNotClickable(...) assertFocused(...) / assertNotFocused(...) etc...
  11. Barista: Real world @Test fun verifyHomeScreen() { DbHelper.setupSomePlayedGames(appDatabase) activityTestRule.launchActivity(null) assertDisplayed(R.id.closestCourseCard)

    assertDisplayed(R.id.onboardingView) clickOn(R.id.viewResults) verifyGameResultsScreen() clickBack() clickOn(R.id.resumeGame) verifyOngoingGameScorecardScreen() clickBack() onView(withId(R.id.home_container)) .perform(swipeUp()) assertDisplayed(R.id.rateAppView) assertDisplayed(R.id.restartOnCourse)
  12. Espresso Test Recorder Easy to get started with No coding

    required Helpful finding view matchers Hard to read generated code Selects obscure elements Almost impossible to update when app changes Easy to become neglected
  13. Espresso UI Tests Pretty easy to get started with Well

    documented Relatively easy to maintain Can test *anything in your app Lots of boilerplate code scrollTo Second class code syndrome Hard to fix if left neglected @Ignore is your worst enemy!
  14. Barista Super simple to work with Well documented Write tests

    you will understand Opensource + 1K Stars / Maintained Some conflicting names with Espresso Not supported by big Company