Your Robotium tests are flaky? not readable? Come to this Tools in Action and discover Espresso, the new testing library for Android. During this live coding presentation, you will learn how to write beautiful tests and take advantage of Espresso features.
Par Thomas Guérin, consultant chez Xebia, à Devoxx Belgique 2014.
3. “My tests last hours !”
#Devoxx #AndroidEspresso @Tom404_
4. “My tests last hours !”
“Testing is sometimes complicated !”
#Devoxx #AndroidEspresso @Tom404_
5. “My tests last hours !”
“Testing is sometimes complicated !”
“My tests randomly fail !”
#Devoxx #AndroidEspresso @Tom404_
6. “My tests last hours !”
“Testing is sometimes complicated !”
“My tests randomly fail !”
“I have to fix the tests for a simple modification in my code”
#Devoxx #AndroidEspresso @Tom404_
7. “My tests last hours !”
“Testing is sometimes complicated !”
“My tests randomly fail !”
“I have to fix the tests for a simple modification in my code”
#Devoxx #AndroidEspresso @Tom404_
8. “My tests last hours !”
“Testing is sometimes complicated !”
“My tests randomly fail !”
“I have to fix the tests for a simple modification in my code”
#Devoxx #AndroidEspresso @Tom404_
9. “My tests last hours !”
“Testing is sometimes complicated !”
“My tests randomly fail !”
“I have to fix the tests for a simple modification in my code”
#Devoxx #AndroidEspresso @Tom404_
10. A test must be …
#Devoxx #AndroidEspresso @Tom404_
11. A test must be …
Simple
#Devoxx #AndroidEspresso @Tom404_
12. A test must be …
Simple Fast
#Devoxx #AndroidEspresso @Tom404_
13. A test must be …
Simple Fast Reliable
#Devoxx #AndroidEspresso @Tom404_
14. A test must be …
Simple Fast Reliable Durable
#Devoxx #AndroidEspresso @Tom404_
15. Bad example
// 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);
#Devoxx #AndroidEspresso @Tom404_
16. Bad example
// 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);
#Devoxx #AndroidEspresso @Tom404_
20. Basic example
solo.clickOnView(R.id.start_activity_button);
// Wait for a specific condition to occur
solo.waitForCondition(new Condition() {
@Override
public boolean isSatisfied() {
return checkConditionInNewActivity();
}
}, Timeout.getSmallTimeout());
solo.clickOnView(R.id.view_in_new_activity);
#Devoxx #AndroidEspresso @Tom404_
21. Basic example
solo.clickOnView(R.id.start_activity_button);
// Wait for a specific condition to occur
solo.waitForCondition(new Condition() {
@Override
public boolean isSatisfied() {
return checkConditionInNewActivity();
}
}, Timeout.getSmallTimeout());
solo.clickOnView(R.id.view_in_new_activity);
#Devoxx #AndroidEspresso @Tom404_
22. Robotium drawbacks
Flaky tests
Hard to extend
View index based methods
Errors not meaningful
#Devoxx #AndroidEspresso @Tom404_
26. Hamcrest
Library of matchers
assertThat("Hello", equalTo("Hello"))
// Syntaxic sugar
assertThat("Hello", is("Hello"))
// A lot of matchers available
allOf not instanceOf hasProperty equalToIgnoringCase
#Devoxx #AndroidEspresso @Tom404_
27. Hamcrest
Library of matchers
assertThat("Hello", equalTo("Hello"))
// Syntaxic sugar
assertThat("Hello", is("Hello"))
// A lot of matchers available
allOf not instanceOf hasProperty equalToIgnoringCase
Easy to compose
#Devoxx #AndroidEspresso @Tom404_
28. Custom matcher
public static Matcher<View> isVisible() {
return new TypeSafeMatcher<View>() {
@Override
public void describeTo(Description description) {
description.appendText("is view visible");
}
@Override
public boolean matchesSafely(View view) {
return view.getVisibility() == View.VISIBLE;
}
};
}
#Devoxx #AndroidEspresso @Tom404_
36. Goodbye flakiness
For each test action, Espresso will :
#Devoxx #AndroidEspresso @Tom404_
37. Goodbye flakiness
For each test action, Espresso will :
1. Wait until the app is idle
#Devoxx #AndroidEspresso @Tom404_
38. Goodbye flakiness
For each test action, Espresso will :
1. Wait until the app is idle
2. Run on the UI thread
#Devoxx #AndroidEspresso @Tom404_
39. Goodbye flakiness
For each test action, Espresso will :
1. Wait until the app is idle
2. Run on the UI thread
3. Wait until completion
#Devoxx #AndroidEspresso @Tom404_
40. Goodbye flakiness
For each test action, Espresso will :
1. Wait until the app is idle
2. Run on the UI thread
3. Wait until completion
4. Check the result
#Devoxx #AndroidEspresso @Tom404_
41. Goodbye flakiness
For each test action, Espresso will :
1. Wait until the app is idle
2. Run on the UI thread
3. Wait until completion
4. Check the result
#Devoxx #AndroidEspresso @Tom404_