Android
Prueba de interfaz de usuario con espresso
Buscar..
Observaciones
Café exprés
La hoja de trucos Espresso te ayudará a escribir tus pruebas y lo que quieres probar:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
También siempre un buen lugar de referencia es la documentación oficial:
https://google.github.io/android-testing-support-library/docs/espresso/index.html
Sugerencias avanzadas de video expreso de Google: https://www.youtube.com/watch?v=isihPOY2vS4
Solución de problemas
- Cuando intente desplazarse, asegúrese de cerrar el teclado primero:
Vigilancia: no usar la versión "Espresso" no hará nada cuando se use fuera de ViewAction. Esto puede no ser obvio si tiene una importación en la versión de ViewAction ya que tienen exactamente el mismo nombre de método.
ViewActions.closeSoftKeyboard;
Espresso.closeSoftKeyboard();
- Al ejecutar pruebas juntas en una suite en lugar de individualmente, tenga en cuenta que la Actividad de la prueba anterior todavía puede estar ejecutándose. No confíe en que se haya llamado a onDestroy () de la prueba anterior antes de las pruebas actuales enResume (). Resulta que esto es realmente un error : http://b.android.com/201513
Preparar espresso
En el archivo build.gradle
de su módulo de aplicación de Android agregue las siguientes dependencias:
dependencies {
// Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// JUnit4 Rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Espresso core
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2.2'
//UI Automator tests
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}
Especificar el AndroidJUnitRunner
para la testInstrumentationRunner
parámetro en el build.gradle
archivo.
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
Además, agregue esta dependencia para proporcionar soporte burlón intencional
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
Y agrega este para soporte de prueba de webview
// Espresso-web for WebView support
androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
Crear clase de prueba de espresso
Coloque la siguiente clase java en src / android Test / java y ejecútelo.
public class UITest {
@Test public void Simple_Test() {
onView(withId(R.id.my_view)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()) // click() is a ViewAction
.check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion
}
}
Abrir Cerrar CajónDisposición
public final class DrawerLayoutTest {
@Test public void Open_Close_Drawer_Layout() {
onView(withId(R.id.drawer_layout)).perform(actionOpenDrawer());
onView(withId(R.id.drawer_layout)).perform(actionCloseDrawer());
}
public static ViewAction actionOpenDrawer() {
return new ViewAction() {
@Override public Matcher<View> getConstraints() {
return isAssignableFrom(DrawerLayout.class);
}
@Override public String getDescription() {
return "open drawer";
}
@Override public void perform(UiController uiController, View view) {
((DrawerLayout) view).openDrawer(GravityCompat.START);
}
};
}
public static ViewAction actionCloseDrawer() {
return new ViewAction() {
@Override public Matcher<View> getConstraints() {
return isAssignableFrom(DrawerLayout.class);
}
@Override public String getDescription() {
return "close drawer";
}
@Override public void perform(UiController uiController, View view) {
((DrawerLayout) view).closeDrawer(GravityCompat.START);
}
};
}
}
Prueba de IU simple expreso
Herramientas de prueba de interfaz de usuario
Dos herramientas principales que actualmente se utilizan principalmente para las pruebas de UI son Appium y Espresso.
Apio | Café exprés |
---|---|
prueba de caja negra | prueba de caja blanca / gris |
lo que ves es lo que puedes probar | puede cambiar el funcionamiento interno de la aplicación y prepararla para la prueba, por ejemplo, guardar algunos datos en la base de datos o las preferencias compartidas antes de ejecutar la prueba |
Se utiliza principalmente para pruebas de integración de extremo a extremo y flujos completos de usuarios. | Probando la funcionalidad de una pantalla y / o flujo. |
se puede abstraer para que la prueba escrita se pueda ejecutar en iOS y Android | Solo Android |
bien apoyado | bien apoyado |
Soporta pruebas en paralelo en múltiples dispositivos con rejilla de selenio. | No fuera de la caja pruebas paralelas, existen complementos como Spoon hasta que sale el verdadero soporte de Google |
Cómo agregar espresso al proyecto
dependencies {
// Set this dependency so you can use Android JUnit Runner
androidTestCompile 'com.android.support.test:runner:0.5'
// Set this dependency to use JUnit 4 rules
androidTestCompile 'com.android.support.test:rules:0.5'
// Set this dependency to build and run Espresso tests
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
// Set this dependency to build and run UI Automator tests
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}
NOTA Si está utilizando las últimas bibliotecas de soporte, anotaciones, etc., debe excluir las versiones anteriores de espresso para evitar colisiones:
// there is a conflict with the test support library (see http://stackoverflow.com/questions/29857695)
// so for now re exclude the support-annotations dependency from here to avoid clashes
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
// exclude a couple of more modules here because of <http://stackoverflow.com/questions/29216327> and
// more specifically of <https://code.google.com/p/android-test-kit/issues/detail?id=139>
// otherwise you'll receive weird crashes on devices and dex exceptions on emulators
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude group: 'com.android.support', module: 'design'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
//excluded specific packages due to https://code.google.com/p/android/issues/detail?id=183454
androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.2') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test:runner:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
androidTestCompile('com.android.support.test:rules:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'support-annotations'
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
exclude module: 'support-v7'
}
Aparte de estas importaciones, es necesario agregar el corredor de pruebas de instrumentación de Android a build.gradle android.defaultConfig:
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Configuración de dispositivo
Para pruebas no escamosas, se recomienda establecer las siguientes configuraciones en sus dispositivos:
- Opciones de desarrollador / Deshabilitar animaciones - reduce la descamación de las pruebas
- Opciones de desarrollador / Manténgase despierto: si tiene dispositivos dedicados para pruebas, esto es útil
- Opciones de desarrollador / Tamaños de búfer del registrador: establezca un número más alto si ejecuta conjuntos de pruebas muy grandes en su teléfono
- Accesibilidad / Retardo de toque y retención: largo para evitar problemas con el toque en el espresso
Bastante una configuración del mundo real ha? Bueno, ahora que está fuera del camino, veamos cómo configurar una pequeña prueba.
Escribiendo la prueba
Supongamos que tenemos la siguiente pantalla: La pantalla contiene:
- campo de entrada de texto - R.id.textEntry
- botón que muestra snackbar con texto escrito cuando se hace clic - R.id.shownSnackbarBtn
- snackbar que debe contener texto escrito por el usuario - android.support.design.R.id.snackbar_text
Ahora vamos a crear una clase que probará nuestro flujo:
/**
* Testing of the snackbar activity.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
//espresso rule which tells which activity to start
@Rule
public final ActivityTestRule<SnackbarActivity> mActivityRule =
new ActivityTestRule<>(SnackbarActivity.class, true, false);
@Override
public void tearDown() throws Exception {
super.tearDown();
//just an example how tear down should cleanup after itself
mDatabase.clear();
mSharedPrefs.clear();
}
@Override
public void setUp() throws Exception {
super.setUp();
//setting up your application, for example if you need to have a user in shared
//preferences to stay logged in you can do that for all tests in your setup
User mUser = new User();
mUser.setToken("randomToken");
}
/**
*Test methods should always start with "testXYZ" and it is a good idea to
*name them after the intent what you want to test
**/
@Test
public void testSnackbarIsShown() {
//start our activity
mActivityRule.launchActivity(null);
//check is our text entry displayed and enter some text to it
String textToType="new snackbar text";
onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
onView(withId(R.id.textEntry)).perform(typeText(textToType));
//click the button to show the snackbar
onView(withId(R.id.shownSnackbarBtn)).perform(click());
//assert that a view with snackbar_id with text which we typed and is displayed
onView(allOf(withId(android.support.design.R.id.snackbar_text),
withText(textToType))) .check(matches(isDisplayed()));
}
}
Como te habrás dado cuenta, hay 3-4 cosas que podrías notar que vienen a menudo:
onView (withXYZ) <- viewMatchers con ellos puedes encontrar elementos en la pantalla
realizar (clic ()) <- verAcciones, puede ejecutar acciones en elementos que encontró anteriormente
cheque (coincide (isDisplayed ())) <- viewAssertions, cheques que desea hacer en las pantallas que encontró anteriormente
Todos estos y muchos otros se pueden encontrar aquí: https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/index.html
Eso es todo, ahora puede ejecutar la prueba haciendo clic derecho en el nombre de la clase / prueba y seleccionando Ejecutar prueba o con el comando:
./gradlew connectedFLAVORNAMEAndroidTest
Arriba navegación
@Test
public void testUpNavigation() {
intending(hasComponent(ParentActivity.class.getName())).respondWith(new Instrumentation.ActivityResult(0, null));
onView(withContentDescription("Navigate up")).perform(click());
intended(hasComponent(ParentActivity.class.getName()));
}
Tenga en cuenta que esta es una solución alternativa y que chocará con otras Vistas que tienen la misma descripción de contenido.
Realizar una acción en una vista
Es posible realizar ViewActions
en una vista usando el método de ejecución.
La clase ViewActions
proporciona métodos de ayuda para las acciones más comunes, como:
ViewActions.click()
ViewActions.typeText()
ViewActions.clearText()
Por ejemplo, para hacer clic en la vista:
onView(...).perform(click());
onView(withId(R.id.button_simple)).perform(click());
Puede ejecutar más de una acción con una llamada de ejecución:
onView(...).perform(typeText("Hello"), click());
Si la vista con la que está trabajando se encuentra dentro de un ScrollView
(vertical u horizontal), considere las acciones anteriores que requieren que la vista se muestre (como click()
y typeText()
) con scrollTo()
. Esto asegura que la vista se muestre antes de continuar con la otra acción:
onView(...).perform(scrollTo(), click());
Encontrar una vista con onView
Con los ViewMatchers
puede encontrar la vista en la jerarquía de vista actual.
Para encontrar una vista, use el método onView()
con un comparador de vista que seleccione la vista correcta. Los métodos onView()
devuelven un objeto de tipo ViewInteraction
.
Por ejemplo, encontrar una vista por su R.id
es tan simple como:
onView(withId(R.id.my_view))
Encontrando una vista con un texto:
onView(withText("Hello World"))
Cafeteras personalizadas espresso
El espresso por defecto tiene muchos emparejadores que lo ayudan a encontrar vistas que necesita para hacer algunas comprobaciones o interacciones con ellas.
Los más importantes se pueden encontrar en la siguiente hoja de trucos:
https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
Algunos ejemplos de matchers son:
- withId (R.id.ID_of_object_you_are_looking_for);
- withText ("Algún texto que esperas que tenga el objeto");
- isDisplayed () <- verifique si la vista está visible
- doesNotExist () <- comprueba que la vista no existe
Todos estos son muy útiles para el uso diario, pero si tiene vistas más complejas, escribir sus emparejadores personalizados puede hacer que las pruebas sean más legibles y pueda reutilizarse en diferentes lugares.
Hay 2 tipos de emparejadores más comunes que puede extender: TypeSafeMatcher BoundedMatcher
La implementación de TypeSafeMatcher requiere que verifique la instancia de la vista contra la que está afirmando, si es del tipo correcto que coincida con algunas de sus propiedades con el valor que proporcionó a un comparador.
Por ejemplo, el tipo de coincidencia segura que valida una vista de imagen tiene dibujable correcto:
public class DrawableMatcher extends TypeSafeMatcher<View> {
private @DrawableRes final int expectedId;
String resourceName;
public DrawableMatcher(@DrawableRes int expectedId) {
super(View.class);
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View target) {
//Type check we need to do in TypeSafeMatcher
if (!(target instanceof ImageView)) {
return false;
}
//We fetch the image view from the focused view
ImageView imageView = (ImageView) target;
if (expectedId < 0) {
return imageView.getDrawable() == null;
}
//We get the drawable from the resources that we are going to compare with image view source
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable == null) {
return false;
}
//comparing the bitmaps should give results of the matcher if they are equal
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
return bitmap.sameAs(otherBitmap);
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
El uso del emparejador podría ser envuelto así:
public static Matcher<View> withDrawable(final int resourceId) {
return new DrawableMatcher(resourceId);
}
onView(withDrawable(R.drawable.someDrawable)).check(matches(isDisplayed()));
Los emparejadores limitados son similares, simplemente no tiene que hacer la verificación de tipo pero, como eso se hace automágicamente para usted:
/**
* Matches a {@link TextInputFormView}'s input hint with the given resource ID
*
* @param stringId
* @return
*/
public static Matcher<View> withTextInputHint(@StringRes final int stringId) {
return new BoundedMatcher<View, TextInputFormView>(TextInputFormView.class) {
private String mResourceName = null;
@Override
public void describeTo(final Description description) {
//fill these out properly so your logging and error reporting is more clear
description.appendText("with TextInputFormView that has hint ");
description.appendValue(stringId);
if (null != mResourceName) {
description.appendText("[");
description.appendText(mResourceName);
description.appendText("]");
}
}
@Override
public boolean matchesSafely(final TextInputFormView view) {
if (null == mResourceName) {
try {
mResourceName = view.getResources().getResourceEntryName(stringId);
} catch (Resources.NotFoundException e) {
throw new IllegalStateException("could not find string with ID " + stringId, e);
}
}
return view.getResources().getString(stringId).equals(view.getHint());
}
};
}
Más sobre los matchers se puede leer en:
https://developer.android.com/reference/android/support/test/espresso/matcher/ViewMatchers.html
Espresso general
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'
ViewMatchers : una colección de objetos que implementan Matcher<? super View>
interfaz. Puede pasar uno o más de estos al método onView
para ubicar una vista dentro de la jerarquía de vista actual.
ViewActions : una colección de ViewActions
que se puede pasar al método ViewInteraction.perform()
(por ejemplo, click()
).
ViewAssertions : una colección de ViewAssertions
que se puede pasar el método ViewInteraction.check()
. La mayoría de las veces, utilizará la aserción de coincidencias, que utiliza un igualador de vista para afirmar el estado de la vista seleccionada actualmente.
Espresso cheat sheet de google
Introduzca texto en EditarTexto
onView(withId(R.id.edt_name)).perform(typeText("XYZ"));
closeSoftKeyboard();
Realizar Clic en Vista
onView(withId(R.id.btn_id)).perform(click());
Se muestra la vista de comprobación
onView(withId(R.id.edt_pan_number)).check(ViewAssertions.matches((isDisplayed())));
Agrupar una colección de clases de prueba en un conjunto de pruebas
Puede organizar la ejecución de sus pruebas de unidades instrumentadas definiendo una Suite .
/**
* Runs all unit tests.
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({MyTest1.class ,
MyTest2.class,
MyTest3.class})
public class AndroidTestSuite {}
Luego, en AndroidStudio puede ejecutar Gradle o establecer una nueva configuración como:
Las suites de prueba se pueden anidar.