Android Testing

3 min read Original article ↗
  • Android Testing

  • Unit Testing Small scoped, isolated

  • public void maximizeVolume(AudioManager audioManager) { if (audioManager.getRingerMode() != RINGER_MODE_SILENT) {

    int max = audioManager.getStreamMaxVolume(STREAM_RING); audioManager.setStreamVolume(STREAM_RING, max, 0); } }

  • JUnit http://junit.org/

  • public void testNormalRingerIsMaximized() { audioManager.setRingerMode(RINGER_MODE_NORMAL); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), max); } !

    public void testSilentRingerIsNotDisturbed() { audioManager.setRingerMode(RINGER_MODE_SILENT); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), 0); }

  • java.lang.RuntimeException: Stub!

  • public void setRingerMode(int mode) { throw new RuntimeException("Stub!"); }

  • Can’t Test Outside A Device!

  • Can’t Test Outside A Device!

  • ^ Can’t Test Outside A Device! Wouldn’t Want To

  • Tests Only Non-Android API's

  • Android Testing Framework https://developer.android.com/tools/testing/ testing_android.html

  • public void testNormalRingerIsMaximized() { audioManager.setRingerMode(RINGER_MODE_NORMAL); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), max); } !

    public void testSilentRingerIsNotDisturbed() { audioManager.setRingerMode(RINGER_MODE_SILENT); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), 0); }

  • Tests Real Android API’s

  • Run on Real Devices

  • Slow!

  • Fast Keep ‘em

  • Robolectric http://robolectric.org/

  • @RunWith(RobolectricTestRunner.class) public void testNormalRingerIsMaximized() { audioManager.setRingerMode(RINGER_MODE_NORMAL); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), max); }

    ! @RunWith(RobolectricTestRunner.class) public void testSilentRingerIsNotDisturbed() { audioManager.setRingerMode(RINGER_MODE_SILENT); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), 0); }

  • @RunWith(RobolectricTestRunner.class) public void testNormalRingerIsMaximized() { audioManager.setRingerMode(RINGER_MODE_NORMAL); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), max); }

    ! @RunWith(RobolectricTestRunner.class) public void testSilentRingerIsNotDisturbed() { audioManager.setRingerMode(RINGER_MODE_SILENT); maximizeVolume(audioManager); assertEquals(audioManager.getStreamVolume(STREAM_RING), 0); }

  • Tests Fail Occasionally.

  • expected: <0> but was: <30>

  • ?

  • Concise Keep ‘em

  • fest-android http://square.github.io/fest-android/

  • @RunWith(RobolectricTestRunner.class) public void testNormalRingerIsMaximized() { audioManager.setRingerMode(RINGER_MODE_NORMAL); maximizeVolume(audioManager); assertThat(audioManager).hasStreamVolume(STREAM_RING, max); }

    ! @RunWith(RobolectricTestRunner.class) public void testSilentRingerIsNotDisturbed() { audioManager.setRingerMode(RINGER_MODE_SILENT); maximizeVolume(audioManager); assertThat(audioManager).hasStreamVolume(STREAM_RING, 0); }

  • @RunWith(RobolectricTestRunner.class) public void testNormalRingerIsMaximized() { audioManager.setRingerMode(RINGER_MODE_NORMAL); maximizeVolume(audioManager); assertThat(audioManager).hasStreamVolume(STREAM_RING, max); }

    ! @RunWith(RobolectricTestRunner.class) public void testSilentRingerIsNotDisturbed() { audioManager.setRingerMode(RINGER_MODE_SILENT); maximizeVolume(audioManager); assertThat(audioManager).hasStreamVolume(STREAM_RING, 0); }

  • Expected ring stream volume <0> but was <30>.

  • Reliable Keep ‘em

  • Mockito https://code.google.com/p/mockito/

  • public void testSilentRingerIsNotDisturbed() { // Prepare mocks and script their

    behavior. AudioManager audioManager = Mockito.mock(AudioManager.class); Mockito.when(audioManager.getRingerMode()) .thenReturn(RINGER_MODE_SILENT); ! // Test the code of interest. maximizeVolume(audioManager); ! // Validate that we saw exactly what we wanted. Mockito.verify(audioManager).getRingerMode(); Mockito.verifyNoMoreInteractions(audioManager); }

  • public void testNormalRingerIsMaximized() { // Prepare mocks and script their

    behavior. AudioManager audioManager = mock(AudioManager.class); when(audioManager.getRingerMode()) .thenReturn(RINGER_MODE_NORMAL); when(audioManager.getStreamMaxVolume(STREAM_RING)) .thenReturn(100); ! // Test the code of interest. maximizeVolume(audioManager); ! // Validate that we saw exactly what we wanted. verify(audioManager) .setStreamVolume(STREAM_RING, 100, 0); }

  • Verify Interactions Also Great for Integration Testing!

  • Integration Testing Larger scoped, verify interactions

  • Monkey https://developer.android.com/tools/help/ monkey.html

  • Not MonkeyRunner! https://developer.android.com/tools/help/ monkeyrunner_concepts.html

  • adb shell monkey -p your.package.name -v 500

  • Low Cost

  • Simple

  • Useless* *well, almost

  • Instrumentation API https://developer.android.com/reference/android/app/ Instrumentation.html

  • public void testPasswordTooShort_ShowsError() { // Make sure the initial state

    does not show any errors. assertThat(username).hasNoError(); assertThat(password).hasNoError(); instrumentation.runOnMainSync(new Runnable() { @Override public void run() { // Type a value into the username and password fields. username.setText("prateek"); password.setText(“password”); // Click the "login" button. login.performClick(); } }); instrumentation.waitForIdleSync(); ! // Verify error was shown only for username field. assertThat(username).hasNoError(); assertThat(password).hasError(R.string.password_length); }

  • Much Boilerplate!

  • Robotium https://code.google.com/p/robotium/

  • public void testPasswordTooShort_ShowsError() { // Make sure the initial state

    does not show any errors. assertThat(username).hasNoError(); assertThat(password).hasNoError(); ! // Type a value into the username and password fields. solo.typeText(username, “prateek”); solo.typeText(password, “password”); // Click the "login" button. solo.clickOnView(login); ! // Verify error was shown only for username field. assertThat(username).hasNoError(); assertThat(password).hasError(R.string.password_length); }

  • Espresso https://code.google.com/p/android-test-kit/wiki/ Espresso

  • Double Espresso https://github.com/JakeWharton/double-espresso

  • public void testPasswordTooShort_ShowsError() { // Make sure the initial state

    does not show any errors. assertThat(username).hasNoError(); assertThat(password).hasNoError(); ! // Type a value into the username and password fields. onView(withId(R.id.username)).perform(typeText("prateek")); onView(withId(R.id.password)).perform(typeText("password")); onView(withId(R.id.login)).perform(click()); ! // Verify error was shown only for username field. assertThat(username).hasNoError(); assertThat(password).hasError(R.string.password_length); }

  • Spoon http://square.github.io/spoon/

  • public void testPasswordTooShort_ShowsError() { Spoon.screenshot(activity, "initial_state"); assertThat(username).hasNoError(); assertThat(password).hasNoError(); ! onView(withId(R.id.username)).perform(typeText("prateek"));

    onView(withId(R.id.password)).perform(typeText("password")); Spoon.screenshot(activity, "values_entered"); onView(withId(R.id.login)).perform(click()); Spoon.screenshot(activity, "login_clicked"); ! assertThat(username).hasNoError(); assertThat(password).hasError(R.string.password_length); }

  • public void testPasswordTooShort_ShowsError() { Spoon.screenshot(activity, "initial_state"); assertThat(username).hasNoError(); assertThat(password).hasNoError(); ! onView(withId(R.id.username)).perform(typeText("prateek"));

    onView(withId(R.id.password)).perform(typeText("password")); Spoon.screenshot(activity, "values_entered"); onView(withId(R.id.login)).perform(click()); Spoon.screenshot(activity, "login_clicked"); ! assertThat(username).hasNoError(); assertThat(password).hasError(R.string.password_length); }

  • Spoon.screenshot(Activity, TAG);

  • None

  • None