Pruebas funcionales en Android con Espresso

miércoles, 3 de diciembre de 2014



En el anterior artículo vimos como podemos hacer pruebas funcionales en Android con Robotium, en este artículo vamos a ver como podemos hacer lo mismo usando Espresso.

Introducción a Espresso

Espresso es un libería que permite realizar pruebas de interfaz de usuario sobre aplicaciones nativas en android. La idea es la misma que Robotium pero esta librería ha sido creada por Google.

Espresso no ha sido publicado como una dependencia Maven por parte de Google. Hay alguna alternativa como la ofrecida por Jake Wharton llamada Double Expresso, pero en este artículo me voy a centrar en descargar el jar de la web oficial de Espresso y vamos a añadirlo manualmente.

Existe una librería indepenciente donde se agrupan Espresso y sus dependencias y luego una librería separada con todas sus dependencias. Además hay dos versiones la versión normal y la contrib, esta última es necesaria si queremos realizar DrawerActions, como abrir y cerrar el drawer lateral que existe en un DrawerLayout.

Instalación de Espresso

Vamos a ver los pasos necesarios para poder utilizar Espresso en un proyecto de Android Studio:

  • Descargar el jar de la web oficial de Espresso. Cómo vamos a realizar pruebas de abrir el Drawer necesitamos la versión contrib. He tenido problemas descargando el jar directamente, así que lo que he tenido que hacer es descargarme el zip con todo el código fuente y coger el fichero espresso-contrib-1.1-bundled.jar de la carpeta \bin\espresso-contrib-standalone.
  • Tenemos que copiar este fichero en la carpeta libs del proyecto, si no existe, hay que crearla al mismo nivel que src. También tenemos que añadir el jar como librería, para hacerlo hacemos click con cotón derecho sobre el fichero jar y elegimos el menu Add as Library.
  • En el fichero build.gradle del modulo app tenemos que tener en la sección de dependencias añadido el jar.

    androidTestCompile files('libs/espresso-contrib-1.1-bundled.jar')
  • Espresso tiene su propio InstrumentationTestRunner entonces debemos configurarlo en la sección Android del mismo fichero build.gradle

                android {
                    defaultConfig {
                        testInstrumentationRunner "com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
            
  • Tenemos que añadir el InstrumentationTestRunner en la configuración de los test de Android. Vamos a menu run/Edit configurations y en la sección de defaults en AndroidTest seleccionamos com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner como InstrumentationTestRunner en el panel de la derecha.
  • Hay que crear los paquetes androidTest/java al mismo nivel que main/java, esta es la ruta por defecto donde debemos ubicar los test.
Con estos pasos ya podemos empezar a crear nuestros test funcionales usando Espresso.

Prototipo de clase de test con Espresso

Cuando utilizamos Espresso, lo normal es que las clases donde tenemos los test tengan un código inicial parecido a este.
public class MainTest extends
        ActivityInstrumentationTestCase2<MainActivity>{

    public MainTest() {
        super(MainActivity.class);
    }

    @Override
    public void setUp(){
        getActivity();
    }

    @Override
    public void tearDown(){
    }

    public void test1(){

    }

    public void test2(){

    }
}


Como podemos ver, la clase hereda de ActivityInstrumentationTestCase2, en el método setUp, que se invoca antes de iniciar cada test, obtenemos el activity,de lo contrario nos dara el error "No activities found. Did you forget to launch the activity by calling getActivity()".

Ejemplo

Para realizar el ejemplo nos vamos a basar el código fuente de una aplicación slide menu de este tutorial, Android Sliding Menu using Navigation Drawer. Esta aplicación es un ejemplo de como realizar un menu lateral utilizando Drawerlayout.

Este es el video de demo de la aplicación que nos servirá para entender las pruebas de interfaz de usuario que vamos a realizar como ejemplo de uso de Robotium.


Probando a abrir y cerrar el menu drawer

Vamos a realizar un test para verificar que se abre y se cierra bien el menu lateral situado en un DrawerLayout

public void testOpenCloseDrawer() {
    // Drawer should not be open to start.
    onView(withId(R.id.drawer_layout)).check(matches(isClosed()));

    openDrawer(R.id.drawer_layout);

    // The drawer should now be open.
    onView(withId(R.id.drawer_layout)).check(matches(isOpen()));

    closeDrawer(R.id.drawer_layout);

    // Drawer should be closed again.
    onView(withId(R.id.drawer_layout)).check(matches(isClosed()));
}


Para realizar este test nos vamos a apoyar en las funciones de la clase DrawerActions. Para verificar el test utilizamos la función OnView para encontrar la vista y la función check para verificar algo en esa vista, son funciones de la clase Espresso. Ambas funciones necesitan unos Machers para identificar la vista o para verificar algo en la vista. El matcher que usamos en onView es withId pasando el id del DrawerLayout y en la función check usamos la función isOpen o isClose.

Verificando que se abre una vista concreta desde su menu del drawerlayout

En este test vamos a abrir el Drawer para selecionar un item y verificaremos que se ha abierto la vista esperada.

public void testVerifyFindPeopleOpenedFromDrawer() {
    openDrawer(R.id.drawer_layout);

    onData(anything())
            .inAdapterView(withId(R.id.list_slidermenu))
            .atPosition(1)
            .perform(ViewActions.click());

    onView(allOf(withText("Find People"),withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))).check(matches(isDisplayed()));
}


En este test primero abrimos el drawer y ahora usamos la función onData de la clase Espresso para buscar un dato, con onView buscamos controles y con onData buscamos contenido. Le estamos indicando que queremos seleccionar la posición 1 del listview con id list_slidermenu y después sobre los datos o vistas podemos hacer una acción usando perform, en este caso indicamos que queremos hacer click.

Después para verificar que se ha abierto la vista esperada, lo que hacemos es buscar el texto Find People, podríamos haber usado onView(withText("Find People")) pero entonces nos habría dado una excepción porque encuentra dos veces el mismo contenido, uno como contenido del fragment abierto y otro el del menu drawer aunque este oculto. Entonces lo que hacemos es usar la función allOf donde podemos especificar más de un matcher y añadimos el matcher withEffectiveVisibility para indicar que la vista del drawer oculto no nos interesa. Finalmente verificamos que con isDisplayed que se esta visualizando en la pantalla la vista seleccionada.

¿que diferencia hay entre el matcher withEffectiveVisibility y isDisplayed? withEffectiveVisibility indica si que todos sus controles parent son visibles y el también aunque haya que hacer scroll para verlo, isDisplayed devuelve true si es visible actualmente en la pantalla sin realizar ninguna acción.

Código disponible en GitHub.

5 comentarios: