En el articulo Android conoce a Jenkins explicamos como configurar Jenkins para que este consultara periódicamente nuestro repositorio en busca de cambios y compilara la aplicación, eso es una pequeña parte de lo que se puede y debe llegar a hacer si queremos desarrollar una aplicación de calidad. Si queremos tomarnos en serio la integración continua tendremos que crear dos tareas nuevas en Jenkins una para la ejecución de pruebas y otra para el análisis de la calidad del código.

Pruebas automatizadas

Las pruebas son una parte vital para asegurarnos que nuestra aplicación cumple con los requisitos, pero a medida que el proyecto va creciendo va dejando de ser eficaz ejecutar las pruebas manualmente desperdiciando así mucho tiempo que puede ser dedicado para otras cosas.

Para las pruebas usaremos Ant, JUnit y Robotium.

En eclipse creamos un nuevo proyecto de pruebas, la creación y conceptos basicos estan explicados en estos dos articulos Testing from Eclipse with ADT y Testing Fundamentals

Ant

En este caso la única diferencia es que tenemos que indicarle a ant que esta vez el proyecto es un proyecto de pruebas.

1
android update test-project -m <main_path> -p <test_path>

Robotium

Robotium es un framework para la realización de pruebas en Android su uso facilita bastante las pruebas (sobre todo las relacionadas con UI). Para poder ejecutarlas en Jenkins sin problemas debemos colocar el jar de robotium en la carpeta libs/ del proyecto de pruebas y subirlo a nuestro repositorio.

Jenkins

La creación de la tarea es igual que la del articulo anterior, para obtener el código tenemos dos opciones obtenerlo directamente desde el repositorio o clonar el workspace de la tare de compilación. En mi caso voy a usar la segunda opción, ya que reduce el tiempo de ejecución y el consumo de ancho de banda. Para poder realizar la clonación tenemos que instalar Clone Workspace SCM Plugin, una vez instalado abrimos la tarea de compilación y en Post-build Actions agregamos la acción Archive for Clone Workspace SCM.
Luego en el proyecto de pruebas dentro de Source Code Management eligiremos Clone Workspace Parent Project y en lista desplegable elegimos el proyecto principal. En Build Triggers hay que indicar que la tarea se iniciara al completarse la tarea anterior, en mi caso el nombre de esa tarea es Finder.


Emulador de Android

Cuando creamos un emulador mediante el SDK de Android, este por defecto se guarda en ~/.android/avd/. Por lo que si creamos un emulador con nuestro usuario este no podrá ser accedido por Jenkins ya que mirara en su home y no en el nuestro.

Lo mas simple es iniciar sesión con Jenkins y crear el emulador con el terminal con la instrucción android create avd. En la documentación podemos consultar con detalle todas las opciones de este comando.

Por ejemplo para crear un emulador con Froyo, ejecutaríamos

1
android avd create -n nombre -t android-8

Dependiendo de nuestra combinación HW+SW podemos encontrarnos con un error bastante molesto, al ejecutar el emulador con el usuario jenkins se generara un segmentation fault. Lo raro es que al ejecutarlo con nuestro usuario no hay ningún problema. Leyendo el informe del bug, podemos ver que se encontró un “workaround” para evitar el error, basta con renombrar la librería ANDROID_SDK/tools/lib/libOpenglRender.so. Al renombrar la librería el emulador no la encontrara por lo que hará el renderizado via software.

Build

Ya tenemos configurado el repositorio y el emulador ahora hay que definir que hacer en la compilación. Para eso agregaremos solo una instruccion en la seccion Build de Jenkins la cualr sera Invoke Ant tendremos que especificar el target como:

1
2
3
4
5
6
7
8
9
10
11
	clean emma debug install test fetch-test-report 
```
- *clean*: limpia el proyecto
- *emma*: indica que se analizara el code coverage
- *debug*: aplicacion en modo debug
- *install*: instala la aplicación en el emulador
- *test*: ejecuta las pruebas
- *fetch-test-report*: obtiene el resultado de las pruebas desde el emulador, lo definiremos mas adelante

Si tenemos las pruebas en el mismo repositorio que la aplicación tendremos que especificar que fichero build.xml usara ant ya que al buscar en la raíz encontrara el correspondiente a la aplicación y no al proyecto de pruebas. Para ello en el campo *Build File* ingresamos *test/build.xml* o la ruta donde se encuentren nuestro proyecto de pruebas.
Finalmente tendremos que definir dos propiedades en el campo *Properties*, la ruta del SDK y el target a usar
sdk.dir=/opt/android-sdk-linux target=android-8 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#### Emma
<a href="http://emma.sourceforge.net/" target="_blank">Emma</a> calculara el code coverage (cobertura de código) , lo cual es realmente útil para saber que partes de nuestro proyecto están cubiertas por las pruebas. Tendremos que instalar <a href="https://wiki.jenkins-ci.org/display/JENKINS/Emma+Plugin" target="_blank">Emma Plugin</a> en Jenkins y agregar en *Post-build Action* la acción *Record Emma coverage report.*
*
*Hay que indicar la ruta donde se encuentra el informe de cobertura, en nuestro caso sera en la carpeta bin del proyecto por lo que seria ***/bin/coverage*.xml*

<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-kMZ3MeGfc74/UKtjuUohm4I/AAAAAAAAAf8/-5gdXYb15n8/s1600/jenkins_emma.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" src="http://3.bp.blogspot.com/-kMZ3MeGfc74/UKtjuUohm4I/AAAAAAAAAf8/-5gdXYb15n8/s400/jenkins_emma.png" width="400" /></a></div>


En caso de que la ruta fuera distinta al ejecutar las pruebas con Emma en la consola de salida se muestra la ruta donde se guardan los informes bastaría con ver donde se guardan y modificar la ruta acorde a nuestro proyecto.

#### JUnit
Para poder mantener un registro de las pruebas realizadas y saber en detalle cuales fallaron y cuales no, Jenkins necesita el resultado de las pruebas en formato XML y ahi es cuando entra en juego <a href="http://zutubi.com/source/projects/android-junit-report/" target="_blank">Android JUnit report</a>, <a href="https://github.com/jsankey/android-junit-report/downloads" target="_blank">bajamos</a> el jar y lo agregamos a la carpeta *libs/* del proyecto y lo subimos al repositorio.

Para configurarlo hay que hacer un par de cambios en nuestro proyecto primero nos vamos a AndroidManifest.xml, buscamos el tag *instrumentation* y cambiamos el atributo *android:name*

android:name="com.zutubi.android.junitreport.JUnitReportTestRunner"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

El segundo paso es crear el fichero *custom_rules.xml* en la carpeta raiz del proyecto


``` xml
<target name="init-props">
<xpath input="${tested.project.dir}/AndroidManifest.xml"
expression="/manifest/@package" output="tested.project.app.package"/>
</target>

<target name="fetch-test-report" depends="init-props">
<echo>Downloading XML test report...</echo>
<mkdir dir="reports"/>
<exec executable="${adb}" failonerror="true">
<arg line="${adb.device.arg}"/>
<arg value="pull"/>
<arg value="/data/data/${tested.project.app.package}/files/junit-report.xml"/>
<arg value="reports/junit-report.xml"/>
</exec>
</target>

Con esto estamos definiendo el target fetch-test-report el cual ejecutara el comando pull en adb para obtener el informe y lo guardara en reports/junit-report.xml.

A esta altura tenemos ant configurado para que obtenga el resultado de las pruebas desde el emulador, lo siguiente es añadir Publish JUnit test result report en Post-build Actions y definir donde encontrar el informe si no modificaron el custom_rules.xml anterior esta ruta seria **/tests/reports/junit-report.xml

Ya estamos listos para ejecutar la tarea, si todo sale bien en el dashboard del proyecto tendremos dos gráficos uno que nos indica el estado de las pruebas y otro el estado del code coverage. Tiene que ejecutarse al menos dos veces para poder ver la gráfica con datos. Al hacer click sobre las graficas se mostraran los detalles de cada informe.

Calidad del código

En esta parte nos centraremos en analizar la calidad de nuestro código, buscando código repetido, comprobando reglas de estilo y encontrando posibles bugs mediante análisis estadístico. Para esto crearemos nuevamente una tarea free-style. La configuración del repositorio y la compilación es igual que el proyecto de pruebas salvo que esta vez la compilación en vez la de ejecutarse al finalizar el proyecto principal se ejecutara al finalizar el proyecto de pruebas.

Nos quedaría definido el siguiente escenario: tenemos tareas, Finder, Finder-test y Finder-code-quality. Al hacer un commit se ejecuta la tarea Finder si no hay fallos se ejecuta Finder-test y nuevamente

Build

Nuevamente a la hora de compilar lo único que haremos sera invocar a Ant. El target sera

1
findbugs pmd lint

Ejecutando findbugs y cpd, a continuación veremos que es cada uno y en el caso que no nos interese ejecutar uno de ellos simplemente borramos el target.

FindBugs

FindBugs tal como su nombre indicara buscara bugs en nuestro código mediante análisis estadístico. Precisaremos el jar findbugs-ant el cual en mi caso no venia incluido en la versión de findbugs de Fedora así que lo baje e instale manualmente. Una vez bajado el archivo y descomprimido en /opt/findbugs-x.y.z donde x.y.z es la versión tendremos que copiar el archivo $FINDBUGS_HOME/lib/findbugs-ant.jar a $ANT_HOME/lib

Antes de ponernos a configurar FindBugs tendremos que instalar el plugin para Jenkins que nos mostrara el informe de manera detallada.

Tendremos que definir en ant el target findbugs, agregando el siguiente código al fichero custom_rules.xml sino existe lo creamos en la raíz de nuestro proyecto NO en el de pruebas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<target name="findbugs" description="Generate the FindBugs XML report">

<fail
message="findbugs.home is missing. Make sure to add it as an argument."
unless="findbugs.home"
/>


<path id="findbugs.jar">
<pathelement path="${findbugs.home}/lib/findbugs-ant.jar">
</pathelement>
</path>

<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpathref="findbugs.jar" />

<echo>FINDBUGS_HOME = ${findbugs.home}</echo>
<mkdir dir="reports" />
<findbugs home="${findbugs.home}" output="xml"
outputFile="reports/findbugs.xml" excludeFilter="findbugs-exclude.xml">

<auxClasspath path="${android.jar}" />
<class location="${basedir}/bin/classes" />
<sourcePath path="${basedir}/src" />
<sourcePath path="${basedir}/tests" />
</findbugs>
</target>

El tag class indica donde se encontrar los archivos .class tenemos dos opciones forzar la compilación diciendo que el target findbugs depende de algún otro target que genere las clases (debug por ejemplo) mediante el atributo depends o usar las clases generadas por la tarea Finder, esta ultima opción es la que yo estoy usando. Por lo que ustedes tendrán que decidir que es lo que prefieren y adaptar la configuración de findbugs.

Al definir este target necesitaremos especificar el valor de algunas variables en el campo Properties de llamada a Ant.

 findbugs.home=/opt/findbugs-2.0.1
android.jar=/opt/android-sdk-linux/platforms/android-8/android.jar

Tenemos que indicar la ruta del jar de Android como classpath auxiliar, es decir, es necesario para el analisis ya que se realizan llamadas a sus métodos en caso de no indicarlo cada vez que se haga una instancia de una clase de Android tendremos un error de missing class.

Y como ultimo paso para ejecutar y obtener los resultados de findbugs en Post-build Actions añadiremos Publish FindBugs analysis results y como ruta de los informes **/reports/findbugs.xml

Open Tasks

Con Open Tasks analizaremos nuestro código en búsqueda de tareas tipo TODO o FIXME, que muchas veces se añaden al código para indicar que falta algo por hacer o hay algo que arreglar y así permanecen durante la eternidad. Luego de instalar el plugin añadiendo Scan workspace for open tasks en Post-build Actions podremos configurar los parámetros de la búsqueda.

Como vemos en la imagen, tendremos que indicar que archivos queremos analizar, cuales excluir y los tags a buscar con su respectiva prioridad.

Checkstyle

El estilo del código también debe tenerse en cuenta a la hora de medir la calidad del mismo. Dependiendo del lenguaje con el que estemos trabajando tendremos un estilo definido para cada uno de ellos. Checkstyle se encargara de analizar este aspecto de nuestro código. Me encontré con el siguiente problema, lo mas raro es que si lo ejecutaba con mi usuario funcionaba sin problemas pero al ejecutarse mediante Jenkins daba ese error. Modificando ANT_HOME el problema se solucionaba parcialmente, ahora si lo ejecutaba con el usuario jenkins funcionaba, pero cuando era el propio Jenkins quien lo ejecutaba el error seguía ahí. La solución que encontré fue instalarlo desde el repositorio de Fedora.
Al igual que con findbugs, tendremos que definir el target para ant

custom_rules.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<target name="checkstyle"
description="Generates a report of code convention violations.">


<fail
message="checkstyle.home is missing. Make sure to add it as an argument."
unless="checkstyle.home" />


<path id="checkstyle.path">
<fileset dir="${checkstyle.home}" includes="*.jar" />
</path>
<taskdef resource="checkstyletask.properties" classpathref="checkstyle.path" />

<checkstyle config="android-checkstyle.xml">
<fileset dir="src" includes="**/*.java" />
<fileset dir="tests" includes="**/*.java" />
<formatter type="xml" toFile="reports/checkstyle.xml" />
</checkstyle>
</target>

No hay un fichero oficial para comprobar el estilo para Android, pero si que hay varios fichero por la vuelta, uno de ellos lo pueden encontrar en android-checkstyle.xml

PMD

PMD analizara el código en busca de posibles errores, variables sin usar, código muerto, demasiado complejo, duplicado, etc. Como siempre descargamos los binarios, y pasamos a agregar el target a Ant. Ya que CPD forma parte de PMD, no usaremos el informe de codigo duplicado ya que se encontrara dentro del informe de PMD.

custo_rules.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<target name="pmd">
<fail
message="pmd.home is missing. Make sure to add it as an argument."
unless="pmd.home"
/>


<path id="pmd.path">
<fileset dir="${pmd.home}/lib" includes="*.jar" />
</path>

<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask"
classpathref="pmd.path" />


<mkdir dir="reports" />
<pmd shortFilenames="true">
<ruleset>rulesets/java/android.xml</ruleset>
<ruleset>rulesets/java/basic.xml</ruleset>
<ruleset>rulesets/java/braces.xml</ruleset>
<ruleset>rulesets/java/clone.xml</ruleset>
<ruleset>rulesets/java/codesize.xml</ruleset>
<ruleset>rulesets/java/comments.xml</ruleset>
<ruleset>rulesets/java/coupling.xml</ruleset>
<ruleset>rulesets/java/design.xml</ruleset>
<ruleset>rulesets/java/empty.xml</ruleset>
<ruleset>rulesets/java/finalizers.xml</ruleset>
<ruleset>rulesets/java/imports.xml</ruleset>
<ruleset>rulesets/java/junit.xml</ruleset>
<ruleset>rulesets/java/naming.xml</ruleset>
<ruleset>rulesets/java/optimizations.xml</ruleset>
<ruleset>rulesets/java/strictexception.xml</ruleset>
<ruleset>rulesets/java/strings.xml</ruleset>
<ruleset>rulesets/java/sunsecure.xml</ruleset>
<ruleset>rulesets/java/typeresolution.xml</ruleset>
<ruleset>rulesets/java/unnecessary.xml</ruleset>
<ruleset>rulesets/java/unusedcode.xml</ruleset>

<formatter type="xml" toFile="reports/pmd.xml" />
<fileset dir="src">
<include name="**/*.java" />
</fileset>
<fileset dir="tests">
<include name="**/*.java" />
</fileset>
</pmd>
</target>

Y mas de lo mismo, indicar en Post-build Actions que queremos publicar el informe generado por PMD.

Y finalmente tenemos todo listo, no es una tarea difícil pero si que puede ser bastante ardua. Sobre todo al principio si nunca habíamos usado Ant haciendo muchos cambios a base de prueba y error.