on the JVM • Easy to integrate with grade and Android Studio • Provides external data mock(http, preference, db) • Binds shadow objects to Android classes(JavaAssist) • Option: Mocito, PowerMock, AssertJ etc…
• Espresso has some components • core, contrib, intents, idling, web • Using DI and makes app testable (Dagger 2 is awesome!!) • Option: assertj-android, Spoon, Mockito • Use AndroidTestCase for simple custom view • Spoon manages multiple devices screenshot
text and then press the button. onView(withId(R.id.editTextUserInput)) .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); onView(withId(R.id.changeTextBt)).perform(click()); // Check that the text was changed. onView(withId(R.id.textToBeChanged)).check(matches(withText(STRING_TO_BE_TYPED))); } @Test public void changeText_newActivity() { // Type text and then press the button. onView(withId(R.id.editTextUserInput)).perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); onView(withId(R.id.activityChangeTextBtn)).perform(click()); // This view is in a different Activity, no need to tell Espresso. onView(withId(R.id.show_text_view)).check(matches(withText(STRING_TO_BE_TYPED))); } Espresso-core
DialerActivity.class); @Before public void stubAllExternalIntents() { // By default Espresso Intents does not stub any Intents. Stubbing needs to be setup before // every test run. In this case all external Intents will be blocked. intending(not(isInternal())).respondWith(new ActivityResult(Activity.RESULT_OK, null)); } @Test public void typeNumber_ValidInput_InitiatesCall() { // Types a phone number into the dialer edit text field and presses the call button. onView(withId(R.id.edit_text_caller_number)) .perform(typeText(VALID_PHONE_NUMBER), closeSoftKeyboard()); onView(withId(R.id.button_call_number)).perform(click()); // Verify that an intent to the dialer was sent with the correct action, phone // number and package. Think of Intents intended API as the equivalent to Mockito's verify. intended(allOf( hasAction(Intent.ACTION_CALL), hasData(INTENT_DATA_PHONE_NUMBER), toPackage(PACKAGE_ANDROID_DIALER))); } Espresso-intents
ShowWebViewActivity.class); @Test public void initial_state() { onWebView().check(webContent(hasElementWithId("email_input"))); onWebView().check(webContent(hasElementWithId("password_input"))); onWebView().check(webContent(hasElementWithId("submit_button"))); } @Test public void enter_email_and_password_submit() { onWebView().withElement(findElement(Locator.ID, "email_input")) .perform(webKeys(EMAIL_TO_BE_TYPED)); onWebView().withElement(findElement(Locator.ID, "password_input")) .perform(webKeys(PASSWORD_TO_BE_TYPED)); onWebView().withElement(findElement(Locator.ID, "submit_button")) .perform(webClick()); // This view is in a different Activity, no need to tell Espresso. onView(withId(R.id.email)).check(matches(withText(EMAIL_TO_BE_TYPED))); onView(withId(R.id.password)).check(matches(withText(PASSWORD_TO_BE_TYPED))); } Espresso-web
ShowWebViewActivity.class); @Test public void initial_state() { onWebView().check(webContent(hasElementWithId("email_input"))); onWebView().check(webContent(hasElementWithId("password_input"))); onWebView().check(webContent(hasElementWithId("submit_button"))); } @Test public void enter_email_and_password_submit() { signInWithIdAndPassword(EMAIL_TO_BE_TYPED, PASSWORD_TO_BE_TYPED); // This view is in a different Activity, no need to tell Espresso. onView(withId(R.id.email)).check(matches(withText(EMAIL_TO_BE_TYPED))); onView(withId(R.id.password)).check(matches(withText(PASSWORD_TO_BE_TYPED))); } private static void signInWithIdAndPassword(String id, String password) { onWebView().withElement(findElement(Locator.ID, "email_input")) .perform(webKeys(id)); onWebView().withElement(findElement(Locator.ID, "password_input")) .perform(webKeys(password)); onWebView().withElement(findElement(Locator.ID, "submit_button")) .perform(webClick()); } Espresso-web Good approach • Don’t write onView each test case • Create your own DSL
/** * An {@link IntentFactory} implementation that wraps all {@code Intent}s with a debug action, which * launches an activity that allows you to inspect the content. */ public final class DebugIntentFactory implements IntentFactory { private final IntentFactory realIntentFactory; private final boolean isMockMode; private final BooleanPreference captureIntents; public DebugIntentFactory(IntentFactory realIntentFactory, boolean isMockMode, BooleanPreference captureIntents) { this.realIntentFactory = realIntentFactory; this.isMockMode = isMockMode; this.captureIntents = captureIntents; } @Override public Intent createUrlIntent(String url) { Intent baseIntent = realIntentFactory.createUrlIntent(url); if (!isMockMode || !captureIntents.get()) { return baseIntent; } else { return ExternalIntentActivity.createIntent(baseIntent); } } } u2020
engineer and iOS app… • Appium • If you have only qa testers… • Calabash-android • If you focus on modern android… • UI automator • Other than that… • robotium-recorder
do retrospective. • Automation is not the silver bullet • We can’t test animation, usability • Do I have to write full set of tests? • It depends on your service level.