Robotiumvs Espresso 
Get ready to rumble !
Thomas Guerin
Thomas Guerin 
Développeur Android
Thomas Guerin 
Développeur Android 
Tests addict
Thomas Guerin 
Développeur Android 
Tests addict 
Niveau 15 à Destiny
Robotium vs Espresso : Get ready to rumble !
Robotium vs Espresso : Get ready to rumble ! 
Pourquoi ne pas faire de tests ? 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Pourquoi ne pas faire de tests ? 
“Mes tests durent des heures !” 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Pourquoi ne pas faire de tests ? 
“Mes tests durent des heures !” 
“Tester ce n’est pas toujours simple !” 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Pourquoi ne pas faire de tests ? 
“Mes tests durent des heures !” 
“Tester ce n’est pas toujours simple !” 
“Mes tests échouent aléatoirement !” 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Pourquoi ne pas faire de tests ? 
“Mes tests durent des heures !” 
“Tester ce n’est pas toujours simple !” 
“Mes tests échouent aléatoirement !” 
“Je vais devoir corriger les tests alors que je 
n’ai presque rien modifié” 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un test doit être… 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un test doit être… 
Simple 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un test doit être… 
Simple Rapide 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un test doit être… 
Simple Rapide Fiable 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un test doit être… 
Simple Rapide Fiable Durable 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un mauvais exemple 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un mauvais exemple 
// Start the main activity of the application under test 
mActivity = getActivity(); 
! 
// Get a handle to the Activity object's main UI widget, a Spinner 
mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01); 
! 
// Set the Spinner to a known position 
mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); 
! 
// Stop the activity - The onDestroy() method should save the state of the Spinner 
mActivity.finish(); 
! 
// Re-start the Activity - the onResume() method should restore the state of the Spinner 
mActivity = getActivity(); 
! 
// Get the Spinner's current position 
int currentPosition = mActivity.getSpinnerPosition(); 
! 
// Assert that the current position is the same as the starting position 
assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Un mauvais exemple 
// Start the main activity of the application under test 
mActivity = getActivity(); 
! 
// Get a handle to the Activity object's main UI widget, a Spinner 
mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01); 
! 
// Set the Spinner to a known position 
mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); 
! 
// Stop the activity - The onDestroy() method should save the state of the Spinner 
mActivity.finish(); 
! 
// Re-start the Activity - the onResume() method should restore the state of the Spinner 
mActivity = getActivity(); 
! 
// Get the Spinner's current position 
int currentPosition = mActivity.getSpinnerPosition(); 
! 
// Assert that the current position is the same as the starting position 
assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Les combattants 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Les combattants 
Robotium 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Les combattants 
#Droidcon #Robotium #Espresso 
VS 
Robotium Espresso 
@Tom404_
Préparation
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Dépendances Gradle 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Dépendances Gradle 
Robotium 
dependencies { 
androidTestCompile ‘com.jayway.android.robotium:robotium-solo:5.2.1' 
androidTestCompile 'com.squareup.assertj:assertj-android:1.0.0' 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Dépendances Gradle 
Espresso 
dependencies { 
/* Jars récupérés depuis https://code.google.com/p/android-test-kit */ 
androidTestCompile files(‘libs/espresso-1.1.jar’, ‘libs/espresso-contrib-1.1.jar', 
‘libs/testrunner-1.1.jar', 'libs/testrunner-runtime-1.1.jar') 
androidTestCompile 'com.google.guava:guava:14.0.1', 
'com.squareup.dagger:dagger:1.1.0', 
'org.hamcrest:hamcrest-core:1.1', 
'org.hamcrest:hamcrest-integration:1.1', 
'org.hamcrest:hamcrest-library:1.1' 
} 
android.packagingOptions { 
exclude 'META-INF/LICENSE.txt' 
exclude 'LICENSE.txt' 
exclude 'META-INF/LICENSE' 
exclude 'META-INF/NOTICE' 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Dépendances Gradle 
Double Espresso 
dependencies { 
androidTestCompile ’com.jakewharton.espresso:espresso:1.1-r3’, 
‘com.jakewharton.espresso:espresso-support-v4:1.1-r3' 
} 
android.packagingOptions { 
exclude 'META-INF/LICENSE.txt' 
exclude 'LICENSE.txt' 
exclude 'META-INF/LICENSE' 
exclude 'META-INF/NOTICE' 
} 
https://github.com/JakeWharton/double-espresso 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Test Runner 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Test Runner 
Robotium utilise le test runner du sdk 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Test Runner 
Robotium utilise le test runner du sdk 
Espresso nécessite un test runner spécifique 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Test Runner 
Robotium utilise le test runner du sdk 
Espresso nécessite un test runner spécifique 
defaultConfig { 
testInstrumentationRunner 
"com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner" 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
GoogleInstrumentationTestRunner 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
GoogleInstrumentationTestRunner 
onCreate de l'application finalisée avant le début des tests 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
GoogleInstrumentationTestRunner 
onCreate de l'application finalisée avant le début des tests 
Instrumentation terminée == activités finies 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
GoogleInstrumentationTestRunner 
onCreate de l'application finalisée avant le début des tests 
Instrumentation terminée == activités finies 
Monitoring des activités plus fiable 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
GoogleInstrumentationTestRunner 
onCreate de l'application finalisée avant le début des tests 
Instrumentation terminée == activités finies 
Monitoring des activités plus fiable 
Peut être utilisé avec d'autres librairies 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Initialisation 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Initialisation 
Robotium 
public class SimpleActionsTest extends ActivityInstrumentationTestCase2<MainActivity> { 
! 
private Solo solo; 
! 
public void setUp() throws Exception { 
super.setUp(); 
solo = new Solo(getInstrumentation(), getActivity()); 
} 
! 
public void tearDown() throws Exception { 
solo.finishOpenedActivities(); 
super.tearDown(); 
} 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Initialisation 
Espresso 
public class SimpleActionsTest extends ActivityInstrumentationTestCase2<MainActivity> { 
! 
@Override 
protected void setUp() throws Exception { 
super.setUp(); 
// Espresso ne va pas lancer l'activité pour nous 
getActivity(); 
} 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Expressivité
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
Robotium 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
#Droidcon #Robotium #Espresso 
Solo 
Robotium 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
clickOnActionBarHome() 
clickOnButton(int index) 
searchText(String text) 
pressSoftKeyboardNextButton() 
clickOnImageButton(int index) 
scrollDownList(int index) 
clearEditText(int index) 
scrollDown() 
clickOnToggleButton(String text) 
pressMenuItem(int index) 
#Droidcon #Robotium #Espresso 
Solo 
getView(int viewId) 
drag(…) 
clickInList(int line, int index) 
goBack() 
setNavigationDrawer(int status) 
clickOnActionBarItem(int id) 
getButton(int index) 
scrollToBottom() searchEditText(String text) 
pressSoftKeyboardSearchButton() 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
// Récupération de la vue 
Button myButton = (TextView) solo.getView(R.id.my_button); 
! 
// Assertions (AssertJ Android) 
assertThat(myButton).hasText(“My button”).isVisible(); 
! 
// Clic sur la vue 
solo.clickOnView(myButton) 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest 
Librairie de matchers 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest 
Librairie de matchers 
assertThat("Hello", equalTo("Hello")) 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest 
Librairie de matchers 
assertThat("Hello", equalTo("Hello")) 
! 
// Sucre syntaxique 
assertThat(“Hello", is("Hello")) 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest 
Librairie de matchers 
assertThat("Hello", equalTo("Hello")) 
! 
// Sucre syntaxique 
assertThat(“Hello", is("Hello")) 
! 
// De nombreux matchers disponibles 
allOf not instanceOf hasProperty equalToIgnoringCase 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest 
Librairie de matchers 
assertThat("Hello", equalTo("Hello")) 
! 
// Sucre syntaxique 
assertThat(“Hello", is("Hello")) 
! 
// De nombreux matchers disponibles 
allOf not instanceOf hasProperty equalToIgnoringCase 
Possibilité de composition 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest custom matcher 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Hamcrest custom matcher 
public class IsNotANumber extends TypeSafeMatcher<Double> { 
! 
@Override 
public boolean matchesSafely(Double number) { 
return number.isNaN(); 
} 
! 
public void describeTo(Description description) { 
description.appendText("not a number"); 
} 
! 
public static Matcher<Double> notANumber() { 
return new IsNotANumber(); 
} 
! 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
Espresso 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
Espresso 
Espresso 
onView(Matcher<View>)! 
onData(Matcher<Object> 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
ViewMatchers Espresso 
Espresso withId()! 
onView(Matcher<View>)! 
onData(Matcher<Object> 
#Droidcon #Robotium #Espresso 
withText(text)! 
hasSibling(Matcher<View>) 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
ViewMatchers Espresso 
Espresso 
onView(Matcher<View>)! 
onData(Matcher<Object> 
#Droidcon #Robotium #Espresso 
withId()! 
withText(text)! 
hasSibling(Matcher<View>) 
ViewActions 
click()! 
typeText(text) 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
ViewMatchers Espresso 
Espresso 
onView(Matcher<View>)! 
onData(Matcher<Object> 
#Droidcon #Robotium #Espresso 
withId()! 
withText(text)! 
hasSibling(Matcher<View>) 
ViewActions 
click()! 
typeText(text) 
ViewAssertions 
doesNotExist()! 
matches(Matcher<View>) 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
ViewMatchers Espresso 
Espresso 
onView(Matcher<View>)! 
onData(Matcher<Object> 
ViewInteraction / DataInteraction 
perform(ViewAction)! 
check(ViewAssertion) 
#Droidcon #Robotium #Espresso 
withId()! 
withText(text)! 
hasSibling(Matcher<View>) 
ViewActions 
click()! 
typeText(text) 
ViewAssertions 
doesNotExist()! 
matches(Matcher<View>) 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
ViewMatchers Espresso 
Espresso 
onView(Matcher<View>)! 
onData(Matcher<Object> 
ViewInteraction / DataInteraction 
perform(ViewAction)! 
check(ViewAssertion) 
#Droidcon #Robotium #Espresso 
withId()! 
withText(text)! 
hasSibling(Matcher<View>) 
ViewActions 
click()! 
typeText(text) 
ViewAssertions 
doesNotExist()! 
matches(Matcher<View>) 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
API overview 
ViewMatchers Espresso 
Espresso 
onView(Matcher<View>)! 
onData(Matcher<Object> 
ViewInteraction / DataInteraction 
perform(ViewAction)! 
check(ViewAssertion) 
#Droidcon #Robotium #Espresso 
withId()! 
withText(text)! 
hasSibling(Matcher<View>) 
ViewActions 
click()! 
typeText(text) 
ViewAssertions 
doesNotExist()! 
matches(Matcher<View>) 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
onView(allOf(withId(R.id.my_button), withText(“My Button”))) 
.check(matches(isDisplayed())) 
.perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
onView(allOf(withId(R.id.my_button), withText(“My Button”))) 
.check(matches(isDisplayed())) 
.perform(click()); 
! 
// Une simple extraction de méthode permet de simplifier 
onView(withIdAndText(R.id.my_button, "My Button”)) 
.check(matches(isDisplayed())) 
.perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
onView(allOf(withId(R.id.my_button), withText(“My Button”))) 
.check(matches(isDisplayed())) 
.perform(click()); 
! 
// Une simple extraction de méthode permet de simplifier 
onView(withIdAndText(R.id.my_button, "My Button”)) 
.check(matches(isDisplayed())) 
.perform(click()); 
! 
// Si le bouton n'est pas affiché le perform(click()) échouera 
onView(withIdAndText(R.id.my_button, "My Button")).perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Exemple 
onView(allOf(withId(R.id.my_button), withText(“My Button”))) 
.check(matches(isDisplayed())) 
.perform(click()); 
! 
// Une simple extraction de méthode permet de simplifier 
onView(withIdAndText(R.id.my_button, "My Button”)) 
.check(matches(isDisplayed())) 
.perform(click()); 
! 
// Si le bouton n'est pas affiché le perform(click()) échouera 
onView(withIdAndText(R.id.my_button, "My Button")).perform(click()); 
Manque de restrictions = AmbiguousViewMatcherException 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Listview 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Listview 
Robotium 
// Clic sur item 
solo.clickOnText("textToFind"); 
! 
// Si jamais il y'en a plusieurs -> utilisation d'un index 
solo.clickOnText("textToFind", 3); 
! 
// Ou choisir directement la ligne 
solo.clickInList(2); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Listview 
Espresso 
onData(allOf(is(instanceOf(String.class)), is("textToFind"))).perform(click()); 
! 
// Ou directement à une position 
onData(is(instanceOf(String.class))).atPosition(0).perform(click()); 
! 
// Possibilité de préciser une listview, utile pour le view pager 
onData(allOf(is(instanceOf(String.class)), is("textToFind"))) 
.inAdapterView(withId(R.id.my_list)).perform(click()); 
! 
// Interaction avec une vue enfant de la ligne 
onData(allOf(is(instanceOf(String.class)), is("textToFind"))) 
.onChildView(withId(R.id.my_child)) 
.perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Listview 
Espresso : cas plus complexe 
// Type renvoyé par la méthode getItem de l'adapteur 
public class Item { 
public String name; 
public String itemContent; 
} 
! 
onData(allOf(is(instanceOf(Item.class)), hasProperty(“name", equalTo(“nameToFind")))) 
.perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Listview 
Espresso : custom matcher 
public static Matcher<Object> withItemName(final Matcher<String> itemTextMatcher){ 
return new BoundedMatcher<Object, Item>(Item.class) { 
@Override 
public boolean matchesSafely(Item item) { 
return itemTextMatcher.matches(item.name); 
} 
! 
@Override 
public void describeTo(Description description) { 
description.appendText("with item name: "); 
itemTextMatcher.describeTo(description); 
} 
}; 
} 
onData(withItemName(equalTo("nameToFind"))).perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Webview 
Robotium 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Webview 
Robotium 
Possibilité d’interagir avec les webviews 
•solo.getWebElements(by) 
•solo.clickOnWebElement(by) 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Webview 
Robotium 
Possibilité d’interagir avec les webviews 
•solo.getWebElements(by) 
•solo.clickOnWebElement(by) 
Plusieurs types de recherches disponibles 
•By.id(String id) 
•By.className(String className) 
•By.textContent(String textContent) 
•By.name(String name) 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Webview 
Espresso 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Webview 
Espresso 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Navigation Drawer 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Navigation Drawer 
Robotium 
// Attention l'id du frame layout du menu doit absolument être "left_drawer" 
solo.setNavigationDrawer(Solo.OPENED); 
! 
solo.setNavigationDrawer(Solo.CLOSED); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Navigation Drawer 
Robotium 
// Attention l'id du frame layout du menu doit absolument être "left_drawer" 
solo.setNavigationDrawer(Solo.OPENED); 
! 
solo.setNavigationDrawer(Solo.CLOSED); 
Espresso 
DrawerActions.openDrawer(R.id.drawer_layout); 
! 
DrawerActions.closeDrawer(R.id.drawer_layout); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Action bar 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Action bar 
Robotium 
solo.clickOnActionBarItem(R.id.action_example); 
! 
// Cas de l'action bar overflow 
solo.sendKey(KeyEvent.KEYCODE_MENU); 
solo.clickOnText("action"); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Action bar 
Robotium 
solo.clickOnActionBarItem(R.id.action_example); 
! 
// Cas de l'action bar overflow 
solo.sendKey(KeyEvent.KEYCODE_MENU); 
solo.clickOnText("action"); 
Espresso 
onView(withId(R.id_action_example)).perform(click()); 
! 
// Cas de l'action bar overflow 
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext()); 
onView(withText("action")).perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Back button & Keyboard 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Back button & Keyboard 
Robotium 
solo.goBack(); 
// solo.goBack() est aussi utilisé pour fermer le clavier 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Back button & Keyboard 
Robotium 
solo.goBack(); 
// solo.goBack() est aussi utilisé pour fermer le clavier 
Espresso 
Espresso.pressBack(); 
Espresso.closeSoftKeyboard(); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Les messages d’erreur 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Les messages d’erreur 
Robotium 
! 
// Récupération d’une vue inexistante 
solo.getView(1234); 
!j 
unit.framework.AssertionFailedError: View with id: '1234' is not found! 
at com.robotium.solo.Solo.getView(Solo.java:2008) 
at com.robotium.solo.Solo.getView(Solo.java:1988) 
at 
fr.droidcon.robotium.vs.espresso.samples.test.robotium.SimpleActionsTest.testClickButton(SimpleAct 
ionsTest.java:28) 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Les messages d’erreur 
Espresso 
! 
// Récupération d’une vue inexistante 
onView(withId(1234)); 
com.google.android.apps.common.testing.ui.espresso.NoMatchingViewException: No views in hierarchy 
found matching: with id: is <1234> 
If the target view is not part of the view hierarchy, you may need to use Espresso.onData to load it from 
one of the following AdapterViews:android.widget.ListView{426f6f18 VFED.VC. ......ID 0,0-720,1557} 
- android.widget.ListView{4269c598 V.ED.VC. ........ 48,393-1032,1509 #7f070042 app:id/my_list} 
! 
View Hierarchy: 
+>DecorView{id=-1, visibility=VISIBLE, width=1080, height=1776, has-focus=true, has-focusable=true, 
has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout- 
requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, 
x=0.0, y=0.0, child-count=1} 
| 
+->ActionBarOverlayLayout{id=16909075, res-name=action_bar_overlay_layout, visibility=VISIBLE, 
width=1080, height=1776, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable= 
false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected= 
false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count= 
3} 
| 
+-->FrameLayout{id=16908290, res-name=content, visibility=VISIBLE, width=1080, height=1557, 
has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is- 
#Droidcon #Robotium #Espresso @Tom404_
Enchaînements
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Wait and See 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Wait and See 
Robotium repose sur le principe de wait & see 
•solo.waitForActivity(Class class, int timeout) 
• solo.waitForCondition(Condition condition, int timeout) 
• solo.waitForDialogToOpen() 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Wait and See 
Robotium repose sur le principe de wait & see 
•solo.waitForActivity(Class class, int timeout) 
• solo.waitForCondition(Condition condition, int timeout) 
• solo.waitForDialogToOpen() 
solo.clickOnView(R.id.start_activity_button); 
! 
// On attend que la vue apparaisse avant d'agir 
solo.waitForView(R.id.view_in_new_activity); 
! 
solo.clickOnView(R.id.view_in_new_activity); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Finis de dormir ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Finis de dormir ! 
Entrée en jeu du GoogleInstrumentationTestRunner 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Finis de dormir ! 
Entrée en jeu du GoogleInstrumentationTestRunner 
Monitoring plus fin des activités et des ressources 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Finis de dormir ! 
Entrée en jeu du GoogleInstrumentationTestRunner 
Monitoring plus fin des activités et des ressources 
Analyse du ThreadUI pour savoir quand agir 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Finis de dormir ! 
Entrée en jeu du GoogleInstrumentationTestRunner 
Monitoring plus fin des activités et des ressources 
Analyse du ThreadUI pour savoir quand agir 
Pas de wait 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Finis de dormir ! 
Entrée en jeu du GoogleInstrumentationTestRunner 
Monitoring plus fin des activités et des ressources 
Analyse du ThreadUI pour savoir quand agir 
Pas de wait 
onView(withId(R.id.start_activity_button)).perform(click()); 
! 
onView(withId(R.id.view_in_new_activity)).perform(click()); 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Idle mais actif ! 
#Droidcon #Robotium #Espresso @Tom404_
Robotium vs Espresso : Get ready to rumble ! 
Idle mais actif ! 
Possibilité d'enregistrer des idlingResources 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
class EspressoThreadPool extends ThreadPoolExecutor implements IdlingResource { 
private int threadCount = 0; 
private ResourceCallback resourceCallback; 
... 
@Override 
public synchronized void execute(Runnable r) { 
threadCount++; 
super.execute(r); 
} 
@Override 
public synchronized boolean isIdleNow() { 
return threadCount == 0; 
} 
@Override 
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 
this.resourceCallback = resourceCallback; 
} 
@Override 
protected synchronized void afterExecute(Runnable r, Throwable t) { 
super.afterExecute(r, t); 
threadCount--; 
if (resourceCallback != null && isIdleNow()) { 
resourceCallback.onTransitionToIdle(); 
} 
} 
} 
#Droidcon #Robotium #Espresso 
@Tom404_
Robotium vs Espresso : Get ready to rumble ! 
class EspressoThreadPool extends ThreadPoolExecutor implements IdlingResource { 
private int threadCount = 0; 
private ResourceCallback resourceCallback; 
... 
@Override 
public synchronized void execute(Runnable r) { 
threadCount++; 
super.execute(r); 
} 
@Override 
public synchronized boolean isIdleNow() { 
return threadCount == 0; 
} 
@Override 
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { 
this.resourceCallback = resourceCallback; 
} 
@Override 
protected synchronized void afterExecute(Runnable r, Throwable t) { 
super.afterExecute(r, t); 
threadCount--; 
if (resourceCallback != null && isIdleNow()) { 
resourceCallback.onTransitionToIdle(); 
} 
} 
} 
Espresso.registerIdlingResources(espressoThreadPool); 
#Droidcon #Robotium #Espresso 
@Tom404_
Conclusion
Questions ?
Robotium vs Espresso: Get ready to rumble ! - DroidCon Paris 2014

Robotium vs Espresso: Get ready to rumble ! - DroidCon Paris 2014

  • 3.
    Robotiumvs Espresso Getready to rumble !
  • 5.
  • 6.
  • 7.
    Thomas Guerin DéveloppeurAndroid Tests addict
  • 8.
    Thomas Guerin DéveloppeurAndroid Tests addict Niveau 15 à Destiny
  • 9.
    Robotium vs Espresso: Get ready to rumble !
  • 10.
    Robotium vs Espresso: Get ready to rumble ! Pourquoi ne pas faire de tests ? #Droidcon #Robotium #Espresso @Tom404_
  • 11.
    Robotium vs Espresso: Get ready to rumble ! Pourquoi ne pas faire de tests ? “Mes tests durent des heures !” #Droidcon #Robotium #Espresso @Tom404_
  • 12.
    Robotium vs Espresso: Get ready to rumble ! Pourquoi ne pas faire de tests ? “Mes tests durent des heures !” “Tester ce n’est pas toujours simple !” #Droidcon #Robotium #Espresso @Tom404_
  • 13.
    Robotium vs Espresso: Get ready to rumble ! Pourquoi ne pas faire de tests ? “Mes tests durent des heures !” “Tester ce n’est pas toujours simple !” “Mes tests échouent aléatoirement !” #Droidcon #Robotium #Espresso @Tom404_
  • 14.
    Robotium vs Espresso: Get ready to rumble ! Pourquoi ne pas faire de tests ? “Mes tests durent des heures !” “Tester ce n’est pas toujours simple !” “Mes tests échouent aléatoirement !” “Je vais devoir corriger les tests alors que je n’ai presque rien modifié” #Droidcon #Robotium #Espresso @Tom404_
  • 15.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 16.
    Robotium vs Espresso: Get ready to rumble ! Un test doit être… #Droidcon #Robotium #Espresso @Tom404_
  • 17.
    Robotium vs Espresso: Get ready to rumble ! Un test doit être… Simple #Droidcon #Robotium #Espresso @Tom404_
  • 18.
    Robotium vs Espresso: Get ready to rumble ! Un test doit être… Simple Rapide #Droidcon #Robotium #Espresso @Tom404_
  • 19.
    Robotium vs Espresso: Get ready to rumble ! Un test doit être… Simple Rapide Fiable #Droidcon #Robotium #Espresso @Tom404_
  • 20.
    Robotium vs Espresso: Get ready to rumble ! Un test doit être… Simple Rapide Fiable Durable #Droidcon #Robotium #Espresso @Tom404_
  • 21.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 22.
    Robotium vs Espresso: Get ready to rumble ! Un mauvais exemple #Droidcon #Robotium #Espresso @Tom404_
  • 23.
    Robotium vs Espresso: Get ready to rumble ! Un mauvais exemple // Start the main activity of the application under test mActivity = getActivity(); ! // Get a handle to the Activity object's main UI widget, a Spinner mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01); ! // Set the Spinner to a known position mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); ! // Stop the activity - The onDestroy() method should save the state of the Spinner mActivity.finish(); ! // Re-start the Activity - the onResume() method should restore the state of the Spinner mActivity = getActivity(); ! // Get the Spinner's current position int currentPosition = mActivity.getSpinnerPosition(); ! // Assert that the current position is the same as the starting position assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition); #Droidcon #Robotium #Espresso @Tom404_
  • 24.
    Robotium vs Espresso: Get ready to rumble ! Un mauvais exemple // Start the main activity of the application under test mActivity = getActivity(); ! // Get a handle to the Activity object's main UI widget, a Spinner mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01); ! // Set the Spinner to a known position mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); ! // Stop the activity - The onDestroy() method should save the state of the Spinner mActivity.finish(); ! // Re-start the Activity - the onResume() method should restore the state of the Spinner mActivity = getActivity(); ! // Get the Spinner's current position int currentPosition = mActivity.getSpinnerPosition(); ! // Assert that the current position is the same as the starting position assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition); #Droidcon #Robotium #Espresso @Tom404_
  • 25.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 26.
    Robotium vs Espresso: Get ready to rumble ! Les combattants #Droidcon #Robotium #Espresso @Tom404_
  • 27.
    Robotium vs Espresso: Get ready to rumble ! Les combattants Robotium #Droidcon #Robotium #Espresso @Tom404_
  • 28.
    Robotium vs Espresso: Get ready to rumble ! Les combattants #Droidcon #Robotium #Espresso VS Robotium Espresso @Tom404_
  • 30.
  • 31.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 32.
    Robotium vs Espresso: Get ready to rumble ! Dépendances Gradle #Droidcon #Robotium #Espresso @Tom404_
  • 33.
    Robotium vs Espresso: Get ready to rumble ! Dépendances Gradle Robotium dependencies { androidTestCompile ‘com.jayway.android.robotium:robotium-solo:5.2.1' androidTestCompile 'com.squareup.assertj:assertj-android:1.0.0' } #Droidcon #Robotium #Espresso @Tom404_
  • 34.
    Robotium vs Espresso: Get ready to rumble ! Dépendances Gradle Espresso dependencies { /* Jars récupérés depuis https://code.google.com/p/android-test-kit */ androidTestCompile files(‘libs/espresso-1.1.jar’, ‘libs/espresso-contrib-1.1.jar', ‘libs/testrunner-1.1.jar', 'libs/testrunner-runtime-1.1.jar') androidTestCompile 'com.google.guava:guava:14.0.1', 'com.squareup.dagger:dagger:1.1.0', 'org.hamcrest:hamcrest-core:1.1', 'org.hamcrest:hamcrest-integration:1.1', 'org.hamcrest:hamcrest-library:1.1' } android.packagingOptions { exclude 'META-INF/LICENSE.txt' exclude 'LICENSE.txt' exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' } #Droidcon #Robotium #Espresso @Tom404_
  • 35.
    Robotium vs Espresso: Get ready to rumble ! Dépendances Gradle Double Espresso dependencies { androidTestCompile ’com.jakewharton.espresso:espresso:1.1-r3’, ‘com.jakewharton.espresso:espresso-support-v4:1.1-r3' } android.packagingOptions { exclude 'META-INF/LICENSE.txt' exclude 'LICENSE.txt' exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' } https://github.com/JakeWharton/double-espresso #Droidcon #Robotium #Espresso @Tom404_
  • 36.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 37.
    Robotium vs Espresso: Get ready to rumble ! Test Runner #Droidcon #Robotium #Espresso @Tom404_
  • 38.
    Robotium vs Espresso: Get ready to rumble ! Test Runner Robotium utilise le test runner du sdk #Droidcon #Robotium #Espresso @Tom404_
  • 39.
    Robotium vs Espresso: Get ready to rumble ! Test Runner Robotium utilise le test runner du sdk Espresso nécessite un test runner spécifique #Droidcon #Robotium #Espresso @Tom404_
  • 40.
    Robotium vs Espresso: Get ready to rumble ! Test Runner Robotium utilise le test runner du sdk Espresso nécessite un test runner spécifique defaultConfig { testInstrumentationRunner "com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner" } #Droidcon #Robotium #Espresso @Tom404_
  • 41.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 42.
    Robotium vs Espresso: Get ready to rumble ! GoogleInstrumentationTestRunner #Droidcon #Robotium #Espresso @Tom404_
  • 43.
    Robotium vs Espresso: Get ready to rumble ! GoogleInstrumentationTestRunner onCreate de l'application finalisée avant le début des tests #Droidcon #Robotium #Espresso @Tom404_
  • 44.
    Robotium vs Espresso: Get ready to rumble ! GoogleInstrumentationTestRunner onCreate de l'application finalisée avant le début des tests Instrumentation terminée == activités finies #Droidcon #Robotium #Espresso @Tom404_
  • 45.
    Robotium vs Espresso: Get ready to rumble ! GoogleInstrumentationTestRunner onCreate de l'application finalisée avant le début des tests Instrumentation terminée == activités finies Monitoring des activités plus fiable #Droidcon #Robotium #Espresso @Tom404_
  • 46.
    Robotium vs Espresso: Get ready to rumble ! GoogleInstrumentationTestRunner onCreate de l'application finalisée avant le début des tests Instrumentation terminée == activités finies Monitoring des activités plus fiable Peut être utilisé avec d'autres librairies #Droidcon #Robotium #Espresso @Tom404_
  • 47.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 48.
    Robotium vs Espresso: Get ready to rumble ! Initialisation #Droidcon #Robotium #Espresso @Tom404_
  • 49.
    Robotium vs Espresso: Get ready to rumble ! Initialisation Robotium public class SimpleActionsTest extends ActivityInstrumentationTestCase2<MainActivity> { ! private Solo solo; ! public void setUp() throws Exception { super.setUp(); solo = new Solo(getInstrumentation(), getActivity()); } ! public void tearDown() throws Exception { solo.finishOpenedActivities(); super.tearDown(); } } #Droidcon #Robotium #Espresso @Tom404_
  • 50.
    Robotium vs Espresso: Get ready to rumble ! Initialisation Espresso public class SimpleActionsTest extends ActivityInstrumentationTestCase2<MainActivity> { ! @Override protected void setUp() throws Exception { super.setUp(); // Espresso ne va pas lancer l'activité pour nous getActivity(); } } #Droidcon #Robotium #Espresso @Tom404_
  • 52.
  • 53.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 54.
    Robotium vs Espresso: Get ready to rumble ! API overview Robotium #Droidcon #Robotium #Espresso @Tom404_
  • 55.
    Robotium vs Espresso: Get ready to rumble ! API overview #Droidcon #Robotium #Espresso Solo Robotium @Tom404_
  • 56.
    Robotium vs Espresso: Get ready to rumble ! API overview clickOnActionBarHome() clickOnButton(int index) searchText(String text) pressSoftKeyboardNextButton() clickOnImageButton(int index) scrollDownList(int index) clearEditText(int index) scrollDown() clickOnToggleButton(String text) pressMenuItem(int index) #Droidcon #Robotium #Espresso Solo getView(int viewId) drag(…) clickInList(int line, int index) goBack() setNavigationDrawer(int status) clickOnActionBarItem(int id) getButton(int index) scrollToBottom() searchEditText(String text) pressSoftKeyboardSearchButton() @Tom404_
  • 57.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 58.
    Robotium vs Espresso: Get ready to rumble ! Exemple #Droidcon #Robotium #Espresso @Tom404_
  • 59.
    Robotium vs Espresso: Get ready to rumble ! Exemple // Récupération de la vue Button myButton = (TextView) solo.getView(R.id.my_button); ! // Assertions (AssertJ Android) assertThat(myButton).hasText(“My button”).isVisible(); ! // Clic sur la vue solo.clickOnView(myButton) #Droidcon #Robotium #Espresso @Tom404_
  • 60.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 61.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest #Droidcon #Robotium #Espresso @Tom404_
  • 62.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest Librairie de matchers #Droidcon #Robotium #Espresso @Tom404_
  • 63.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest Librairie de matchers assertThat("Hello", equalTo("Hello")) #Droidcon #Robotium #Espresso @Tom404_
  • 64.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest Librairie de matchers assertThat("Hello", equalTo("Hello")) ! // Sucre syntaxique assertThat(“Hello", is("Hello")) #Droidcon #Robotium #Espresso @Tom404_
  • 65.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest Librairie de matchers assertThat("Hello", equalTo("Hello")) ! // Sucre syntaxique assertThat(“Hello", is("Hello")) ! // De nombreux matchers disponibles allOf not instanceOf hasProperty equalToIgnoringCase #Droidcon #Robotium #Espresso @Tom404_
  • 66.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest Librairie de matchers assertThat("Hello", equalTo("Hello")) ! // Sucre syntaxique assertThat(“Hello", is("Hello")) ! // De nombreux matchers disponibles allOf not instanceOf hasProperty equalToIgnoringCase Possibilité de composition #Droidcon #Robotium #Espresso @Tom404_
  • 67.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 68.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest custom matcher #Droidcon #Robotium #Espresso @Tom404_
  • 69.
    Robotium vs Espresso: Get ready to rumble ! Hamcrest custom matcher public class IsNotANumber extends TypeSafeMatcher<Double> { ! @Override public boolean matchesSafely(Double number) { return number.isNaN(); } ! public void describeTo(Description description) { description.appendText("not a number"); } ! public static Matcher<Double> notANumber() { return new IsNotANumber(); } ! } #Droidcon #Robotium #Espresso @Tom404_
  • 70.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 71.
    Robotium vs Espresso: Get ready to rumble ! API overview Espresso #Droidcon #Robotium #Espresso @Tom404_
  • 72.
    Robotium vs Espresso: Get ready to rumble ! API overview Espresso Espresso onView(Matcher<View>)! onData(Matcher<Object> #Droidcon #Robotium #Espresso @Tom404_
  • 73.
    Robotium vs Espresso: Get ready to rumble ! API overview ViewMatchers Espresso Espresso withId()! onView(Matcher<View>)! onData(Matcher<Object> #Droidcon #Robotium #Espresso withText(text)! hasSibling(Matcher<View>) @Tom404_
  • 74.
    Robotium vs Espresso: Get ready to rumble ! API overview ViewMatchers Espresso Espresso onView(Matcher<View>)! onData(Matcher<Object> #Droidcon #Robotium #Espresso withId()! withText(text)! hasSibling(Matcher<View>) ViewActions click()! typeText(text) @Tom404_
  • 75.
    Robotium vs Espresso: Get ready to rumble ! API overview ViewMatchers Espresso Espresso onView(Matcher<View>)! onData(Matcher<Object> #Droidcon #Robotium #Espresso withId()! withText(text)! hasSibling(Matcher<View>) ViewActions click()! typeText(text) ViewAssertions doesNotExist()! matches(Matcher<View>) @Tom404_
  • 76.
    Robotium vs Espresso: Get ready to rumble ! API overview ViewMatchers Espresso Espresso onView(Matcher<View>)! onData(Matcher<Object> ViewInteraction / DataInteraction perform(ViewAction)! check(ViewAssertion) #Droidcon #Robotium #Espresso withId()! withText(text)! hasSibling(Matcher<View>) ViewActions click()! typeText(text) ViewAssertions doesNotExist()! matches(Matcher<View>) @Tom404_
  • 77.
    Robotium vs Espresso: Get ready to rumble ! API overview ViewMatchers Espresso Espresso onView(Matcher<View>)! onData(Matcher<Object> ViewInteraction / DataInteraction perform(ViewAction)! check(ViewAssertion) #Droidcon #Robotium #Espresso withId()! withText(text)! hasSibling(Matcher<View>) ViewActions click()! typeText(text) ViewAssertions doesNotExist()! matches(Matcher<View>) @Tom404_
  • 78.
    Robotium vs Espresso: Get ready to rumble ! API overview ViewMatchers Espresso Espresso onView(Matcher<View>)! onData(Matcher<Object> ViewInteraction / DataInteraction perform(ViewAction)! check(ViewAssertion) #Droidcon #Robotium #Espresso withId()! withText(text)! hasSibling(Matcher<View>) ViewActions click()! typeText(text) ViewAssertions doesNotExist()! matches(Matcher<View>) @Tom404_
  • 79.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 80.
    Robotium vs Espresso: Get ready to rumble ! Exemple #Droidcon #Robotium #Espresso @Tom404_
  • 81.
    Robotium vs Espresso: Get ready to rumble ! Exemple onView(allOf(withId(R.id.my_button), withText(“My Button”))) .check(matches(isDisplayed())) .perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 82.
    Robotium vs Espresso: Get ready to rumble ! Exemple onView(allOf(withId(R.id.my_button), withText(“My Button”))) .check(matches(isDisplayed())) .perform(click()); ! // Une simple extraction de méthode permet de simplifier onView(withIdAndText(R.id.my_button, "My Button”)) .check(matches(isDisplayed())) .perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 83.
    Robotium vs Espresso: Get ready to rumble ! Exemple onView(allOf(withId(R.id.my_button), withText(“My Button”))) .check(matches(isDisplayed())) .perform(click()); ! // Une simple extraction de méthode permet de simplifier onView(withIdAndText(R.id.my_button, "My Button”)) .check(matches(isDisplayed())) .perform(click()); ! // Si le bouton n'est pas affiché le perform(click()) échouera onView(withIdAndText(R.id.my_button, "My Button")).perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 84.
    Robotium vs Espresso: Get ready to rumble ! Exemple onView(allOf(withId(R.id.my_button), withText(“My Button”))) .check(matches(isDisplayed())) .perform(click()); ! // Une simple extraction de méthode permet de simplifier onView(withIdAndText(R.id.my_button, "My Button”)) .check(matches(isDisplayed())) .perform(click()); ! // Si le bouton n'est pas affiché le perform(click()) échouera onView(withIdAndText(R.id.my_button, "My Button")).perform(click()); Manque de restrictions = AmbiguousViewMatcherException #Droidcon #Robotium #Espresso @Tom404_
  • 85.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 86.
    Robotium vs Espresso: Get ready to rumble ! Listview #Droidcon #Robotium #Espresso @Tom404_
  • 87.
    Robotium vs Espresso: Get ready to rumble ! Listview Robotium // Clic sur item solo.clickOnText("textToFind"); ! // Si jamais il y'en a plusieurs -> utilisation d'un index solo.clickOnText("textToFind", 3); ! // Ou choisir directement la ligne solo.clickInList(2); #Droidcon #Robotium #Espresso @Tom404_
  • 88.
    Robotium vs Espresso: Get ready to rumble ! Listview Espresso onData(allOf(is(instanceOf(String.class)), is("textToFind"))).perform(click()); ! // Ou directement à une position onData(is(instanceOf(String.class))).atPosition(0).perform(click()); ! // Possibilité de préciser une listview, utile pour le view pager onData(allOf(is(instanceOf(String.class)), is("textToFind"))) .inAdapterView(withId(R.id.my_list)).perform(click()); ! // Interaction avec une vue enfant de la ligne onData(allOf(is(instanceOf(String.class)), is("textToFind"))) .onChildView(withId(R.id.my_child)) .perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 89.
    Robotium vs Espresso: Get ready to rumble ! Listview Espresso : cas plus complexe // Type renvoyé par la méthode getItem de l'adapteur public class Item { public String name; public String itemContent; } ! onData(allOf(is(instanceOf(Item.class)), hasProperty(“name", equalTo(“nameToFind")))) .perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 90.
    Robotium vs Espresso: Get ready to rumble ! Listview Espresso : custom matcher public static Matcher<Object> withItemName(final Matcher<String> itemTextMatcher){ return new BoundedMatcher<Object, Item>(Item.class) { @Override public boolean matchesSafely(Item item) { return itemTextMatcher.matches(item.name); } ! @Override public void describeTo(Description description) { description.appendText("with item name: "); itemTextMatcher.describeTo(description); } }; } onData(withItemName(equalTo("nameToFind"))).perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 91.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 92.
    Robotium vs Espresso: Get ready to rumble ! Webview Robotium #Droidcon #Robotium #Espresso @Tom404_
  • 93.
    Robotium vs Espresso: Get ready to rumble ! Webview Robotium Possibilité d’interagir avec les webviews •solo.getWebElements(by) •solo.clickOnWebElement(by) #Droidcon #Robotium #Espresso @Tom404_
  • 94.
    Robotium vs Espresso: Get ready to rumble ! Webview Robotium Possibilité d’interagir avec les webviews •solo.getWebElements(by) •solo.clickOnWebElement(by) Plusieurs types de recherches disponibles •By.id(String id) •By.className(String className) •By.textContent(String textContent) •By.name(String name) #Droidcon #Robotium #Espresso @Tom404_
  • 95.
    Robotium vs Espresso: Get ready to rumble ! Webview Espresso #Droidcon #Robotium #Espresso @Tom404_
  • 96.
    Robotium vs Espresso: Get ready to rumble ! Webview Espresso #Droidcon #Robotium #Espresso @Tom404_
  • 97.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 98.
    Robotium vs Espresso: Get ready to rumble ! Navigation Drawer #Droidcon #Robotium #Espresso @Tom404_
  • 99.
    Robotium vs Espresso: Get ready to rumble ! Navigation Drawer Robotium // Attention l'id du frame layout du menu doit absolument être "left_drawer" solo.setNavigationDrawer(Solo.OPENED); ! solo.setNavigationDrawer(Solo.CLOSED); #Droidcon #Robotium #Espresso @Tom404_
  • 100.
    Robotium vs Espresso: Get ready to rumble ! Navigation Drawer Robotium // Attention l'id du frame layout du menu doit absolument être "left_drawer" solo.setNavigationDrawer(Solo.OPENED); ! solo.setNavigationDrawer(Solo.CLOSED); Espresso DrawerActions.openDrawer(R.id.drawer_layout); ! DrawerActions.closeDrawer(R.id.drawer_layout); #Droidcon #Robotium #Espresso @Tom404_
  • 101.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 102.
    Robotium vs Espresso: Get ready to rumble ! Action bar #Droidcon #Robotium #Espresso @Tom404_
  • 103.
    Robotium vs Espresso: Get ready to rumble ! Action bar Robotium solo.clickOnActionBarItem(R.id.action_example); ! // Cas de l'action bar overflow solo.sendKey(KeyEvent.KEYCODE_MENU); solo.clickOnText("action"); #Droidcon #Robotium #Espresso @Tom404_
  • 104.
    Robotium vs Espresso: Get ready to rumble ! Action bar Robotium solo.clickOnActionBarItem(R.id.action_example); ! // Cas de l'action bar overflow solo.sendKey(KeyEvent.KEYCODE_MENU); solo.clickOnText("action"); Espresso onView(withId(R.id_action_example)).perform(click()); ! // Cas de l'action bar overflow openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext()); onView(withText("action")).perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 105.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 106.
    Robotium vs Espresso: Get ready to rumble ! Back button & Keyboard #Droidcon #Robotium #Espresso @Tom404_
  • 107.
    Robotium vs Espresso: Get ready to rumble ! Back button & Keyboard Robotium solo.goBack(); // solo.goBack() est aussi utilisé pour fermer le clavier #Droidcon #Robotium #Espresso @Tom404_
  • 108.
    Robotium vs Espresso: Get ready to rumble ! Back button & Keyboard Robotium solo.goBack(); // solo.goBack() est aussi utilisé pour fermer le clavier Espresso Espresso.pressBack(); Espresso.closeSoftKeyboard(); #Droidcon #Robotium #Espresso @Tom404_
  • 109.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 110.
    Robotium vs Espresso: Get ready to rumble ! Les messages d’erreur #Droidcon #Robotium #Espresso @Tom404_
  • 111.
    Robotium vs Espresso: Get ready to rumble ! Les messages d’erreur Robotium ! // Récupération d’une vue inexistante solo.getView(1234); !j unit.framework.AssertionFailedError: View with id: '1234' is not found! at com.robotium.solo.Solo.getView(Solo.java:2008) at com.robotium.solo.Solo.getView(Solo.java:1988) at fr.droidcon.robotium.vs.espresso.samples.test.robotium.SimpleActionsTest.testClickButton(SimpleAct ionsTest.java:28) #Droidcon #Robotium #Espresso @Tom404_
  • 112.
    Robotium vs Espresso: Get ready to rumble ! Les messages d’erreur Espresso ! // Récupération d’une vue inexistante onView(withId(1234)); com.google.android.apps.common.testing.ui.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: is <1234> If the target view is not part of the view hierarchy, you may need to use Espresso.onData to load it from one of the following AdapterViews:android.widget.ListView{426f6f18 VFED.VC. ......ID 0,0-720,1557} - android.widget.ListView{4269c598 V.ED.VC. ........ 48,393-1032,1509 #7f070042 app:id/my_list} ! View Hierarchy: +>DecorView{id=-1, visibility=VISIBLE, width=1080, height=1776, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout- requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1} | +->ActionBarOverlayLayout{id=16909075, res-name=action_bar_overlay_layout, visibility=VISIBLE, width=1080, height=1776, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable= false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected= false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count= 3} | +-->FrameLayout{id=16908290, res-name=content, visibility=VISIBLE, width=1080, height=1557, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is- #Droidcon #Robotium #Espresso @Tom404_
  • 114.
  • 115.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 116.
    Robotium vs Espresso: Get ready to rumble ! Wait and See #Droidcon #Robotium #Espresso @Tom404_
  • 117.
    Robotium vs Espresso: Get ready to rumble ! Wait and See Robotium repose sur le principe de wait & see •solo.waitForActivity(Class class, int timeout) • solo.waitForCondition(Condition condition, int timeout) • solo.waitForDialogToOpen() #Droidcon #Robotium #Espresso @Tom404_
  • 118.
    Robotium vs Espresso: Get ready to rumble ! Wait and See Robotium repose sur le principe de wait & see •solo.waitForActivity(Class class, int timeout) • solo.waitForCondition(Condition condition, int timeout) • solo.waitForDialogToOpen() solo.clickOnView(R.id.start_activity_button); ! // On attend que la vue apparaisse avant d'agir solo.waitForView(R.id.view_in_new_activity); ! solo.clickOnView(R.id.view_in_new_activity); #Droidcon #Robotium #Espresso @Tom404_
  • 119.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 120.
    Robotium vs Espresso: Get ready to rumble ! Finis de dormir ! #Droidcon #Robotium #Espresso @Tom404_
  • 121.
    Robotium vs Espresso: Get ready to rumble ! Finis de dormir ! Entrée en jeu du GoogleInstrumentationTestRunner #Droidcon #Robotium #Espresso @Tom404_
  • 122.
    Robotium vs Espresso: Get ready to rumble ! Finis de dormir ! Entrée en jeu du GoogleInstrumentationTestRunner Monitoring plus fin des activités et des ressources #Droidcon #Robotium #Espresso @Tom404_
  • 123.
    Robotium vs Espresso: Get ready to rumble ! Finis de dormir ! Entrée en jeu du GoogleInstrumentationTestRunner Monitoring plus fin des activités et des ressources Analyse du ThreadUI pour savoir quand agir #Droidcon #Robotium #Espresso @Tom404_
  • 124.
    Robotium vs Espresso: Get ready to rumble ! Finis de dormir ! Entrée en jeu du GoogleInstrumentationTestRunner Monitoring plus fin des activités et des ressources Analyse du ThreadUI pour savoir quand agir Pas de wait #Droidcon #Robotium #Espresso @Tom404_
  • 125.
    Robotium vs Espresso: Get ready to rumble ! Finis de dormir ! Entrée en jeu du GoogleInstrumentationTestRunner Monitoring plus fin des activités et des ressources Analyse du ThreadUI pour savoir quand agir Pas de wait onView(withId(R.id.start_activity_button)).perform(click()); ! onView(withId(R.id.view_in_new_activity)).perform(click()); #Droidcon #Robotium #Espresso @Tom404_
  • 126.
    Robotium vs Espresso: Get ready to rumble ! #Droidcon #Robotium #Espresso @Tom404_
  • 127.
    Robotium vs Espresso: Get ready to rumble ! Idle mais actif ! #Droidcon #Robotium #Espresso @Tom404_
  • 128.
    Robotium vs Espresso: Get ready to rumble ! Idle mais actif ! Possibilité d'enregistrer des idlingResources #Droidcon #Robotium #Espresso @Tom404_
  • 129.
    Robotium vs Espresso: Get ready to rumble ! class EspressoThreadPool extends ThreadPoolExecutor implements IdlingResource { private int threadCount = 0; private ResourceCallback resourceCallback; ... @Override public synchronized void execute(Runnable r) { threadCount++; super.execute(r); } @Override public synchronized boolean isIdleNow() { return threadCount == 0; } @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } @Override protected synchronized void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); threadCount--; if (resourceCallback != null && isIdleNow()) { resourceCallback.onTransitionToIdle(); } } } #Droidcon #Robotium #Espresso @Tom404_
  • 130.
    Robotium vs Espresso: Get ready to rumble ! class EspressoThreadPool extends ThreadPoolExecutor implements IdlingResource { private int threadCount = 0; private ResourceCallback resourceCallback; ... @Override public synchronized void execute(Runnable r) { threadCount++; super.execute(r); } @Override public synchronized boolean isIdleNow() { return threadCount == 0; } @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } @Override protected synchronized void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); threadCount--; if (resourceCallback != null && isIdleNow()) { resourceCallback.onTransitionToIdle(); } } } Espresso.registerIdlingResources(espressoThreadPool); #Droidcon #Robotium #Espresso @Tom404_
  • 132.
  • 134.