icono_twiter
Jose Manuel Sánchez Suárez

Consultor tecnológico de desarrollo de proyectos informáticos.

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2013-09-12

Tutorial visitado 2.157 veces Descargar en PDF
Ejecución de tests de integración en aplicaciones OSGI con el soporte de Arquilian.

Ejecución de tests de integración en aplicaciones OSGI con el soporte de Arquilian.


0. Índice de contenidos.


1. Introducción

Hace poco ya vimos cómo ejecutar tests de integración con el soporte de Arquilian en aplicaciones Java bajo el entorno de CDI, el estandar de inyección de dependencias de JEE, y Weld, su implementación de referencia. En este tutorial vamos a hacer uso también de Arquilian pero para ejecutar tests de integración en aplicaciones java bajo un entorno OSGI.

OSGI (Open Services Gateway Initiative) define un sistema modular para las aplicaciones Java estableciendo un estandar para la creación de servicios, empaquetados en módulos (bundles) y una forma de interactuar estos servicios entre sí en tiempo de ejecución.

Esos bundles de OSGI no son más que las clásicas librerías o empaquetaciones, jars, que hacen uso de un fichero que describe las características que va a tener dentro del contenedor de OSGI: versión del bundle, servicios que expone y consume,... ese fichero es el archifamoso MANIFEST.MF, que hasta ahora no habías usado para nada ;)

Algunas de las características básicas de un bundle dentro de un contenedor OSGI son las siguientes:

  • tiene su propio classloader independiente del resto de bundles, con lo que las librerías de una empaquetación no afectan a otras,
  • la instalación, arranque, desinstalación de un bundle se realiza dinámicamente en tiempo de ejecución,
  • se pueden facetar para que tengan características web y desplegar una aplicación web dinámica.

El contenedor OSGI por excelencia es Apache Equinox, la base del IDE Eclipse, pero existen otras alternativas open source y los servidores de aplicaciones dan soporte para despliegue de las mismas como bundles de OSGI, así:

Y hasta aquí vamos a llegar en relación a OSGI, ya entraremos en mayor detalle más adelante, porque en este primer tutorial solo queremos exponer cómo realizar tests de integración de servicios OSGI con el soporte de un contenedor embebido, estableciendo una clara distinción entre que es un test unitario y en qué se diferencia de un test de integración.


2. Entorno.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15' (2.4 GHz Intel Core i7, 8GB DDR3 SDRAM).
  • Sistema Operativo: Mac OS X Lion 10.7.4
  • Java(TM) SE Runtime Environment (build 1.7.0_11-b21)

3. Nuestro primer servicio: diferenciando entre tests unitarios y de integración.

Vamos a comenzar con el test, que es lo que toca:

package com.autentia.osgi.tutorial;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class CalculadoraTest 
{
    
    @Test
    public void dadoDosEnterosCuandoSeSumaDevuelveLaAdiccionDeLosMismos() {
    
        final Calculadora calculadora = new Calculadora();
        
        assertEquals(5, calculadora.suma(3, 2));
    }
    
}

Bueno, ya estamos en ROJO y sí, nuestro servicio va a ser tan simple.

Y ahora vamos a crear nuestro servicio que cumpla con los requisitos del test:

package com.autentia.osgi.tutorial;

class Calculadora {

	public int suma(int x, int y) {
		return x + y ;
	}

}

Si ejecutamos el test pasaríamos a VERDE, lo refactorizaremos más adelante cuando elevemos esta calculadora al nivel de un servicio OSGI.

¿Demasiado simple?, pues esto es un test unitario. Si nuestra calculadora dependiera de otro servicio deberíamos mockearlo, si no, no es un test unitario.


4. Creando nuestro primer servicio OSGI.

Ya tenemos un servicio simple y queremos exponerlo como un servicio OSGI dentro del contenedor en el que se ejecutan el resto de bundles para que puedan hacer uso del mismo.

Lo primero que vamos a hacer que crear una interfaz para nuestra calculadora puesto que va a ser la interfaz del servicio la que vamos a exponer en el bundle, de hecho ya habíamos reducido la visibilidad del servicio a nivel de paquete.

Esta es nuestra interfaz de servicio:

package com.autentia.osgi.tutorial;

public interface CalculadoraService {

	int suma (int x, int y);	
}

Y modificamos el servicio marcándolo para que implemente la interfaz:

package com.autentia.osgi.tutorial;

class Calculadora implements CalculadoraService{

	public int suma(int x, int y) {
		return x + y ;
	}

}

El último paso es exponer el servicio en el contenedor y para ello debemos registrarlo en el contexto de bundles, añadiéndolo a la clase Activator, esta clase es la que se marca en el MANIFEST.MF como Bundle-Activator:

Bundle-Activator: com.autentia.osgi.tutorial.Activator

El contenido de la clase sería el siguiente:

package com.autentia.osgi.tutorial;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

public class Activator implements BundleActivator {

    ServiceRegistration<?> serviceRegistration;

	public void start(BundleContext context) {
		serviceRegistration = context.registerService(CalculadoraService.class.getName(), new Calculadora(), null);
	}

	public void stop(BundleContext context) {
		serviceRegistration.unregister();
	}

}

Ya lo tenemos, ¿y ahora cómo lo probamos?, ¿levantamos el servidor de aplicaciones para probarlo?; en nuestro caso el servicio es muy atómico, pero si dependiese de otro servicio o tuviera más dependencias del contenedor osgi, ¿como aseguramos su funcionalidad?. La respuesta es un test de integración que permita recuperar la interfaz del servicio del contenedor sin estar acoplado con la implementación concreta.


5. Test de integración con el soporte de Arquilian.

Lo primero, como siempre, es añadir las dependencias de las librerías necesarias para ejecutar el test de integración en el ámbito de test que, usando maven, bastará con incluir las siguientes en nuestro pom.xml.

<dependency>
	<groupId>org.jboss.arquillian.junit</groupId>
	<artifactId>arquillian-junit-container</artifactId>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.jboss.arquillian.container</groupId>
	<artifactId>arquillian-osgi-embedded</artifactId>
	<version>2.0.0.CR4</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.jboss.osgi.framework</groupId>
	<artifactId>jbosgi-framework-core</artifactId>
	<version>3.0.0.CR1</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.10</version>
	<scope>test</scope>
</dependency>

Si recordamos del tutorial de Arquilian la implementación concreta del contenedor en la que se ejecutarán las pruebas se descubre automáticamente del classloader, no es necesario configurar nada más, si bien, usando como hacemos el contenedor de OSGI de Jboss, necesitamos incluir en el path del entorno de test.

  • un fichero arquilian.xml con el siguiente contenido:
    <arquillian xmlns="http://jboss.org/schema/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
    
        <container qualifier="jboss" default="true">
            <configuration>
                <property name="frameworkProperties">src/test/resources/jbosgi-framework.properties</property>
            </configuration>
        </container>
    </arquillian>
    
  • un fichero jbosgi-framework.properties en el path indicado en el fichero anterior con el siguiente contenido:
    # Properties to configure the Framework
    org.osgi.framework.storage=./target/osgi-store
    org.osgi.framework.storage.clean=onFirstInit
    
    # Extra System Packages
    org.osgi.framework.system.packages.extra=org.jboss.logging;version\=3.0,org.slf4j;version\=1.6
    

Ya lo tenemos todo y ahora, vamos con el código del test:

package com.autentia.osgi.tutorial;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.io.InputStream;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.osgi.metadata.OSGiManifestBuilder;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.Asset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

@RunWith(Arquillian.class)
public class CalculadoraIntegrationTest
{
    @Deployment
    public static JavaArchive createdeployment() {
        final JavaArchive archive = ShrinkWrap.create(JavaArchive.class, "test.jar");
        archive.addClasses(Calculadora.class, CalculadoraService.class, Activator.class);
        archive.setManifest(new Asset() {
            public InputStream openStream() {
                OSGiManifestBuilder builder = OSGiManifestBuilder.newInstance();
                builder.addBundleSymbolicName(archive.getName());
                builder.addBundleManifestVersion(2);
                builder.addBundleVersion("1.0.0");
                builder.addBundleActivator(Activator.class.getName());
                return builder.openStream();
            }
        });
        return archive;
    }

    
    @Test
    public void dadoDosEnterosCuandoSeSumaDevuelveLaAdiccionDeLosMismos(@ArquillianResource Bundle bundle) throws Exception {
        assertNotNull("Bundle injected", bundle);
        
        bundle.start();
        assertEquals("Bundle ACTIVE", Bundle.ACTIVE, bundle.getState());
        BundleContext context = bundle.getBundleContext();

        final ServiceReference sref = context.getServiceReference(CalculadoraService.class.getName());

        final CalculadoraService calculadora = (CalculadoraService) context.getService(sref);

        assertNotNull("Service not null", calculadora);
        
        assertEquals(5, calculadora.suma(3, 2));
    }
    
}

A destacar:

  • el método marcado con la anotación @Deployment crea la empquetación del bundle "al vuelo" con las clases que queremos probar, y del mismo modo se crea y se da contenido al MANIFEST.MF,
  • el método anotado con @Test recibe como parámetro la instancia del bundle desplegado, lo arrancamos invocando al método start y podemos recuperar los servicios que contiene para testarlos.

Termina probando lo mismo que comprobábamos con el test unitario, pero obteniendo el servicio del contexto de OSGI del contenedor embebido, con lo que estamos probando tambien la lógica de la clase Activator y, lo más importante, dejamos abierta la vía para comprobar el resto de servicios que sí tienen dependencias con otros servicios expuestos por el mismo u otros bundles.


6. Referencias.


7. Conclusiones.

No creais que nos faltaría nada más, puesto que como con Arquilian seleccionamos las clases que formarán parte del bundle, podemos mockear la lógica de clases o servicios que dependan de infraestructuras de entorno.

Espero que os sea de utilidad.

Un saludo.

Jose

jmsanchez@autentia.com

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL: