Blog de Antonio Manuel Muñiz

Desarrollo, Ingeniería y Calidad del Software

Archivos mensuales: julio 2008

Exploratory Testing: tests al vuelo

El concepto de Exploratory Testing se adapta a la perfección al tipo de tests requeridos si se sigue una metodología ágil en el desarrollo de software.

El exploratory testing, como su nombre indica, consiste en la aplicación de un enfoque exploratorio y medianamente superficial a la definición pruebas, en contraposición al testing “profundo” o “tradicional”, que se centra en una funcionalidad y la prueba exhaustivamente mediante pesados scripts o planes de pruebas.

En concreto se trata de indentificar a grandes rasgos los principales módulos funcionales de la aplicación a probar, definiendo una serie de pruebas que no entran en detalles ni casos que se prevé que serán poco usados. De esta forma se puede definir todo un conjunto se tests que cubren gran parte de la funcionalidad básica de la aplicación.

La ventaja del exploratory testing, además de ser más ameno para el tester que el testing “profundo”, es que de una forma temprana podemos obtener un conjunto de pruebas que abarcan todo el sistema desarrollado, de esta forma se encuentran bugs de forma rápida y temprana. Este enfoque es aplicable a proyectos cuyos requisitos no son completos desde el instante 0, ya que los tests se centran en los requisitos de más alto nivel, que generalmente no cambian a lo largo del desarrollo.

Evidentemente también hay contras. El principal inconveniente es que la cobertura de código cae en picado, con el consecuente riesgo de dejar de lado problemas potenciales de la aplicación que pueden surgir en fases más tardías.

Decía al comienzo que el exploratory testing se adapta a la perfección a una metodología ágil de desarrollo de software, el enfoque es claro, podemos reducir el tiempo de definición y ejecución de pruebas, pudiendo realizar una prueba completa de la aplicación en cada iteración o hito del proyeto, podemos adaptar el plan de pruebas a los requisitos que cambien en cada hito de forma sencilla. Al ahorrar tiempo (y dinero) en las fases anteriores, el equipo de calidad puede dedicarse a preparar un buen plan de implantación lo más sencillo y automatizado posible (por ejemplo).

Como decía Aristóteles, “en el término medio esta la virtud”, por eso la realidad (al menos lo que vivo día a día) es que lo óptimo viene a ser una mezcla sutil de ambos enfoques, el pesado y el exploratorio, consiguiendo un equilibrio entre formalidad y agilidad.

Spring beans como Web Services con WSO2 Framework

Hace algún tiempo hablaba de WSO2 y su gran iniciativa en el mundo de los servicios web. He estado probando Web Services Framework for Spring y la verdad es que han conseguido que la árdua tarea de integrar un servicio web basado en Apache Axis2 con Spring sea cuestión de minutos.

Para ilustrar el proceso configuraremos una aplicación web basada en Spring para que exponga uno de sus beans como un servicio web.

La estructura de nuestra aplicación será la siguiente:

+META-INF
+WEB-INF
   + lib
   - applicationContext.xml
   - dispatcher-servlet.xml
   - axis2Config.xml
   - web.xml
+axis2-web
-index.jsp

En WEB-INF/lib debemos copiar el directorio /lib distribuido con el framework. Sería posible evitar esto si nuestro proyecto esta modelado con maven, aprovechando que WSF-Spring también es un proyecto Maven 2, para esto habría que estudiar más a fondo la estructura de WSF-Spring e integrarlo como un módulo de nuestro proyecto.

También debemos copiar el fichero axis2Config.xml a nuestro WEB-INF, y por último copiar el directorio axis2-web (incluido en la distribución) a la raíz de nuestro proyecto.

El bean que expondremos como un servicio será el siguiente:

package sample.spring.axis;
public class Sample {

    private int offset;

    public int add(int i, int j){
        return offset + i + j;
    }

    public void setOffset(int value){
        this.offset = value;
    }
}

Se ha incluido el atributo offset para comprobar como se comporta el servicio manteniendo un estado interno representado mediante esta variable.

El siguiente paso es la inicialización del bean en el contexto de la aplicación (applicationContext.xml):

<bean id="addBean" class="sample.spring.axis.Sample">
    <property name="offset" value="0" />
</bean>

En principio el valor de offset será 0 y la configuración necesaria para mostrar el bean como un servicio será:

<bean id="services" class="org.wso2.spring.ws.WebServices">
    <property name="services">
        <list>
            <bean id="helloService" class="org.wso2.spring.ws.SpringWebService">
                <property name="serviceBean" ref="addBean" />
                <property name="serviceName" value="addBeanService" />
            </bean>
        </list>
    </property>
</bean>

Importar el fichero axis2Config.xml en el applicationContext.xml:

<import resource="axis2Config.xml"/>

En el fichero web.xml de nuestra aplicación incluiremos:

<servlet>
    <servlet-name>axis2</servlet-name>
    <servlet-class>org.wso2.spring.ws.servlet.SpringAxis2Servlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>axis2</servlet-name>
    <url-pattern>/axis2/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>axis2</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

Y nada más. Esta es toda la configuración necesaria, si empaquetamos la aplicación web en un WAR y la desplegamos en un servidor de aplicaciones podemos comprobar que el servicio se esta exponiendo correctamente accediendo a http://<dirección_ip&gt;:<puerto>/<contexto>/services/addBeanService?wsdl

Yo lo he desplegado en un servidor Glassfish (donde también tengo una instancia de Hudson ;) dicho sea de paso):

El WSDL se muestra en la URL indicada:

Para probar que el servicio funciona correctamente haremos una petición con SoapUI:

Se puede observar que el servicio generado por WSF-Spring muestra como operaciones todos los métodos públicos del bean, en este caso add y setOffset. Mediante una llamada a setOffset modificaremos la propiedad offset del bean (simulación de estado):

Si volvemos a realizar la llamada a la operación add con los parámetros 2, 3 por ejemplo comprobamos que efectivamente es posible modificar el estado del bean expuesto como servicio.

Sonar y Continuum (La calidad bajo control II)

Hace algún tiempo comentaba cómo Sonar proporciona una serie de medidas de calidad del software. Integrar esta herramienta en el proceso de integración continua modelado con Apache Continuum no ha sido tan fácil como esperaba, sin embargo el resultado merece la pena.

En primer lugar tenemos que instalar y configurar Sonar 1.4RC1. La configuración por defecto hace que la aplicación use una base de datos en ficheros (derby). Dada la restricción impuesta para la configuración con derby haremos que Sonar utilice una base de datos MySQL, esta configuración será necesaria si Sonar y Continuum están en hosts distintos. Debemos comentar en el fichero sonar.properties las lineas:

#sonar.jdbc.url:  jdbc:derby://localhost:1527/sonar;create=true
#sonar.jdbc.driver:  org.apache.derby.jdbc.ClientDriver

y descomentar (sustituyendo <host> y <port> por el host y el puerto de la base de datos):

sonar.jdbc.url:  jdbc:mysql://<host>:<port>/sonar?autoReconnect=true&useUnicode=true&characterEncoding=utf8
sonar.jdbc.driver:  com.mysql.jdbc.Driver

En el fichero sonar.properties también podemos configurar el puerto y el path de la aplicación:

sonar.web.port:  80
sonar.web.context:  /sonar

Para terminar con la configuración debemos crear una base de datos llamada “sonar” y un usuario con permisos que pueda conectarse desde cualquier host.

Ahora debemos configurar nuestro proyecto Maven para ser desplegado en Sonar. Para ello incluiremos un perfil en el fichero POM:

<profile>
    <id>sonar</id>
    <activation>
        <property>
            <name>env</name>
            <value>sonar</value>
        </property>
    </activation>
    <properties>
        <!-- URL de la instancia de Sonar -->
        <sonar.host.url>http://<sonar_host>:<port>/<path></sonar.host.url>
        <!-- URL de la base de datos -->
        <sonar.jdbc.url>jdbc:mysql://<db_host>:<port>/sonar</sonar.jdbc.url>
        <!-- Driver para MySQL -->
        <sonar.jdbc.driver>com.mysql.jdbc.Driver</sonar.jdbc.driver>
        <!-- Usuario de base de datos con permisos sobre la BD "sonar" -->
        <sonar.jdbc.username>username</sonar.jdbc.username>
        <!-- Password el usuario anterior -->
        <sonar.jdbc.password>password</sonar.jdbc.password>
    </properties>
</profile>

Para desplegar el proyecto en Sonar ejecutaremos:

mvn -Psonar org.codehaus.sonar:sonar-maven-plugin:1.4RC1:sonar

En este se ha usado el plugin para la versión 1.4RC1 de Sonar, la versión del plugin debe ser la misma que la del servidor.

Si incluimos esta tarea en Continuum para que se ejecute cada noche entonces cada mañana tendremos un reciente análisis estático del código de nuestro proyecto.

Continuum

Incluir tarea en Continuum

Procesos de integración continua en Java

Cuando un proyecto Java se incluye en un proceso de integración continua hay una decisión que tomar: ¿qué “builds” definir?.
En principio parece lógico que el proyecto debe, al menos, compilar correctamente. Por tanto tenemos nuestro primer “build”, compilación.

En segundo lugar, la ejecución de las pruebas unitarias, indispensables en cualquier proyecto el que se sigue un proceso de QA básico. Segundo “build”, ejecución de pruebas unitarias.

Tercero, generación de reportes estáticos. Recopilar la información obtenida de un análisis estático del código. Tercer “build”, análisis estático (JavaNCSS, FindBugs, CheckStyle, PMD)

Cuarto, centralización de la información. Recopilar toda la información anterior y colocarla de forma ordenada y accesible. Cuarto “build”, ordenar la información (Maven Site).

Quinto, despliegue de la aplicación en caso de ser una aplicación web. De esta forma siempre habrá una instancia de la aplicación desplegada que contiene los últimos cambios sobre el código. Quinto “build”, despliegue del proyecto (Cargo).

Evidentemente, la clave para la automatización de todo lo anterior es Maven (con los correspondientes plugins) y una herramienta de integración continua (Continuum, Gump, CruiseControl, …)