«

»

jun 17 2013

Introducción al API de testing en Android

Cada día aparecen decenas de aplicaciones nuevas en los markets de Android y existe una gran competencia para captar el interés de usuarios, blogs, páginas de noticias, etc. Un error en una aplicación puede tener consecuencias catastróficas, por eso debemos cuidar no solo aspectos de diseño, sino también asegurar que la aplicación funcionará correctamente.

androcode_test1

El SDK de Android cuenta con las herramientas necesarias para escribir distintos tipos de prueba sobre nuestras aplicaciones. En esta entrada daremos un repaso global a estas herramientas. En posteriores entradas profundizaremos en herramientas y técnicas para escribir pruebas en Android.

Empezamos aquí una serie de entradas cuyo objetivo será explicar las distintas herramientas y cómo utilizarlas para escribir pruebas para aplicaciones Android. En esta entrada, la primera de la serie, haremos un recorrido por los elementos que el API de Android ofrece para escribir pruebas y veremos nuestros primeros ejemplos.

Una rápida introducción a las pruebas en Android

Si no conoces nada sobre pruebas del software (o software testing en inglés), en las próximas líneas tienen un curso concentrado con todo lo que necesitas para ponerte manos a la obra.

El framework más popular para escribir pruebas en Java es JUnit y, tanto Eclipse como Android, traen integrado JUnit versión 3.x. En esta versión de JUnit una prueba es un método que comienza por “test” y pertenece a una clase que extiende la clase de JUnit TestCase. Un método de prueba se divide en tres partes: preparar, actuar y verificar (traducción libre de asume-act-assert). Veamos esto en un ejemplo de una prueba que verifica el método add de HashSet y que tienes a continuación.

public class TestHashSet extends TestCase {
	public void testAdd() {
		// Preparar
		Set<Integer> s = new HashSet<>();
		// Actuar
		s.add(42);
		// Verificar
		assertEquals(1, s.size());
	}

}

Los asserts son métodos especiales para verificar condiciones que hacen que las pruebas sean más compactas y sencillas de leer. En el ejemplo anterior, si el método size devuelve un valor distinto de 1, la prueba se marca como errónea y, al final tendremos un mensaje de error.

Ahora que sabemos qué es una prueba, veamos cómo hacerlas en Android. Los pasos para escribir una nueva prueba en Android (y que detallaremos a continuación) son:

  1. Crear un nuevo proyecto de prueba asociado al proyecto que queremos probar.
  2. Crear una nueva clase extendiendo la clase de prueba adecuada.
  3. Escribir nuestras pruebas.
  4. Ejecutar la clase de pruebas como Android JUnit Test (esto ejecutará automáticamente todos los métodos de prueba).

Ejecutar pruebas en Eclipse

El API de testing de Android es el conjunto de clases del SDK de Android que nos permite a escribir pruebas con Junit para aplicaciones Android. La mayoría de estas clases las podemos encontrar en el paquete android.test (documentación: http://developer.android.com/reference/android/test/package-summary.html). De manera global, este API se basa en cuatro grades pilares: el proyecto de pruebas, las clases base de prueba, los mocks y los asserts. Vamos a comentar todos estos elementos y a ver ejemplos en las próximas secciones.

El proyecto de pruebas

Como hemos visto antes, para empezar a escribir prueba el primer paso es crear un proyecto de prueba vinculado al proyecto que contiene la aplicación a probar. Así mantenemos el código de prueba alejado del código principal.

Intrumentación de una aplicación

Para probar una aplicación la ejecutamos instrumentada, es decir, con una capa que nos permita monitorizar y controlar el entorno de ejecución y permita a las pruebas interactuar con el código de la aplicación de una manera más sencilla y flexible. El asistente de creación del proyecto de prueba se encarga de configurar el manifest adecuadamente.

Clases base de prueba y mocks

En función de lo que queramos probar deberemos elegir una clase base concreta para extender con nuestra clase de pruebas. Vamos a darle un rápido repaso a las clases más importantes (de color verde en el diagrama de clases) y qué es lo que prueban cada una.

Jerarquía básica de clases de prueba

La clase de prueba básica es android.test.TestCase (la misma que usamos en el ejemplo de prueba del HashSet). Esta clase es la clase adecuada para cuando queremos probar código que no depende de ninguna característica concreta del entorno, por ejemplo lógica de negocio o conexión a una URL como en el siguiente ejemplo.

public class TestHttpGet extends TestCase {

	public void testHttpGet() {
		HttpGet httpget = new HttpGet("https://github.com");
		HttpClient httpclient = new DefaultHttpClient();
		HttpResponse response = null;

		try {
			response = httpclient.execute(httpget);
		} catch(Exception e) {
			fail("Excepcion");
		}

		assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode() );
	}

}

Otra clase base de prueba muy habitual es android.test.AndroidTestCase. Esta clase extiende a la clase anterior y ofrece un contexto simulado, ya preparado para pruebas por lo que es la clase adecuada para probar cualquier código que dependa del contexto, como acceso a SQLite, URIs, etc.

Por ejemplo, la siguiente prueba verifica que tenemos el permiso de usuario ACCESS_NETWORK_STATE (escribo este tipo de pruebas  después de estar una hora buscando un error dando por hecho que tenía bien establecido los permisos). En este caso utilizamos el contexto para acceder a los paquetes de la aplicación

public class TestUserPermissions extends AndroidTestCase 
{
	public void testPermissionsACCESS_NETWORK_STATE_isInManifest() {
		pm = this.getContext().getPackageManager();
		int res = pm.checkPermission("android.permission.ACCESS_NETWORK_STATE", "org.miaplicacion"); 
		assertEquals(res, PackageManager.PERMISSION_GRANTED );
	}
}

Una clase de prueba más compleja es android.test.ActivityInstrumentationTestCase2. Con esta clase probamos Activities. La propia clase se encarga de crear el objeto Activity y de inicializarlo adecuadamente pasando por todos sus estados.

Por ejemplo, en la siguiente prueba verificamos que los dos atributos de MainActivity se han inicializado correctamente lo cual significa que, al ejecutar la prueba esta actividad ya ha pasado por las fases create y start.

public class MainActivity extends Activity {
	List<String> cadenas;
	String estado;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		//...
		cadenas = new ArrayList<String>();		
	}

	@Override
	protected void onStart() {
		//...
		estado = "Running";
	}
}
public class TestMainActivityLifecycle extends ActivityInstrumentationTestCase2<MainActivity> {
	public void testInitlization() {
		MainActivity ma = this.getActivity();

		assertNotNull(ma.getCadenas());
		assertEquals("Running", ma.getEstado());
	}
}

Además de las tres clases anteriores, existen otras para probar código más especializado como android.test.ServiceTestCase para probar servicios o android.test.ProviderTestCase2 para probar proveedores de contenido (derivados de ContentProvider) . Puedes encontrar un listado completo en la propia documentación del API de Android.

Otros elementos necesarios a la hora de escribir pruebas son los mocks. Un mock es una clase que simula el comportamiento de otra clase más compleja, por ejemplo una clase que recupere datos a través de Internet o de una base de datos, o cuya información no esté disponible durante una prueba. En Android, los mocks nos permiten simular el contexto, escribir datos en sistemas de ficheros, etc.

Los mocks más habituales para cada caso base suelen venir ya implementados en el propio caso base para facilitar el trabajo. Por ejemplo, la clase AndroidTestCase nos proporciona un mock del contexto ya creado a través del método getContext(). Puedes consultar el API de Android para ver más mocks en este enlace: http://developer.android.com/reference/android/test/mock/package-summary.html

Nuevos Asserts

Además de las clases de prueba anteriores, Android proporciona un conjunto propio de asserts en las clases MoreAsserts y ViewAsserts. La primera clase, MoreAsserts, incluye asserts genéricos para trabajar con colecciones o con expresiones regulares. Veamos un ejemplo a continuación.

public class TestMoreAsserts extends TestCase {
	public void testAllElementsAreContentInAnyOrder() {
		List<Integer> org = Arrays.asList(1, 2, 3, 4);
		MoreAsserts.assertContentsInAnyOrder(org, 4, 3, 2, 1);
		// fail -- MoreAsserts.assertContentsInAnyOrder(org, 4, 3, 2);
		// fail -- MoreAsserts.assertContentsInAnyOrder(org, 4, 3, 2, 1, 5);
	}
}

La prueba anterior verifica que todos los elementos indicados a partir del segundo parámetro están contenidos en la lista indicada como primer parámetro. Además, en los comentarios, se muestran dos ejemplos que harían fallar a la prueba.

La segunda clase, ViewAsserts, define asserts para comprobar la disposición y alineación de elementos en las vistas. Veamos un ejemplo a continuación.

public class TestMainActivityView extends ActivityInstrumentationTestCase2<MainActivity> {

	public void testTitle() {
		MainActivity ma = this.getActivity();
		View origin = ma.getWindow().getDecorView();

		View title = ma.findViewById(R.id.title);

		ViewAsserts.assertOnScreen(origin, title);
	}

}

La prueba anterior verifica que el componente de la vista con id title está visible en la pantalla.

Conclusiones

El API de pruebas de Android no termina aquí. En los paquetes android.test y android.test.mock encontrarás más clases para escribir pruebas en Android. Además, existen herramientas adicionales, como Monkeyrunner, para simular la interacción con el dispositivo, Robolectric, para ejecutar las pruebas sin arrancar el emulador, etc.

En posteriores entradas iremos presentando y explicando algunas de estas herramientas. Mientras tanto, ya tienes lo necesario para empezar a escribir tus primeras pruebas y hacer aún mejores aplicaciones para Android.

Nos vemos en la próxima entrada.

Los ejemplos de código han sido tomados del proyecto Android Testing Snippets (URL: https://github.com/javierj/android-test-snippets), un repositorio de GitHub que almacena pequeños casos de prueba a modo de ejemplos. Si quieres contribuir con código o quieres proponer un nuevo snippet puedes ponerte en contacto con el autor.

Acerca del autor

Javier Gutiérrez

Javier Gutiérrez es investigador en la Universidad de Sevilla y su principal área de investigación es el testing de software. Entre otras actividades, escribe un libro gratuito sobre Test-Driven Development, organiza el dojo de código DojoUS y colabora escribiendo y mejorando el código fuente de varios proyectos libres.

  • http://www.androcode.es Inmaculada Alcón

    Muy buen trabajo!! Tenemos que hacer test unitarios para nuestros proyectos!! ;)

  • http://iwt2-javierj.tumblr.com Javier Gutiérrez

    Gracias Inmaculada, estoy completamente de acuerdo 8D

    Si no hay ningún imprevisto, vamos a seguir escribiendo de pruebas aquí, pero también hay que dar la lata en otros frentes, organizar dojos de código, charlas, encuentros, ponerse pesado con las empresas, convencer a los empleados para que sean motores de cambio y de mejora en su organización, etc.

    Un saludo.

  • http://gravatar.com/jorgeantoniotj Jorge

    Muy buen articulo, ojala y puedas publicar pronto la continuación. Saludos!!!

    • http://iwt2-javierj.tumblr.com Javier J.

      Gracias Jorge, la idea original era publicar más continuaciones, pero todo se ha quedado para durante julio y agosto. a ver si lo voy poniendo en marcha este septiembre. Un saludo

  • gopacSI

    Espera muy pronto la NUEVA versión de Delphi XE5 de Embarcadero Technologies con la que podrás crear aplicaciones para iOS, Windows y Android sin modificar tu código. Para mas información visita nuestra pagina web http://gopac.com.mx/v3/index.asp