Cómo hacer testing automático de un applet Java

0
11070

Índice de contenidos

1. Introducción

Los Applets son aplicaciones Java que se ejecutan dentro de un navegador con una serie de restricciones de seguridad, como por ejemplo no poder acceder al sistema de ficheros local. Si bien este tipo de aplicaciones ya está en desuso gracias a los avances en HTML5, CSS3, y JavaScript, pueden ser muy útiles para complementar la funcionalidad de nuestras aplicaciones Web, ya que permiten hacer cosas que con JavaScript son muy complicadas o incluso imposibles (acceder al sistema de ficheros, manipulación de certificados, gestión de dispositivos conectados al ordenador, …).

En este tutorial vamos a ver como podemos hacer tests automáticos de un applet teniendo en cuenta sus particularidades de ejecución.

Para el tutorial he preparado el siguiente repositorio de GitHub https://github.com/alejandropg/testing-applet

En el repositorio podemos encontrar la siguiente estructura:

Donde podemos destacar:

  • demo/ – contienen los ejemplos para probar las librerías, así como los tests de sistema que prueban el ciclo completo levantando un FireFox y ejecutando los tests automáticos con SeleniumHQ. Nada que se encuentre dentro de este directorio debería acabar en el código de producción, es decir sólo son ejemplos de cómo usar el código que se encuentra en el resto de directorios.
  • dria/Driven Rich Internet Application. Pequeña librería de testing, desarrollada aquí para simular la navegación e interacción con el applet. Tanto atacando a la clase del applet directamente con Swing, como atacando al applet dentro de un navegador a través de LiveConnect y JavaScript.
  • fest/FEST es un conjunto de librerías para facilitar el testing. En este directorio encontraremos unas clases que nos ayudarán a probar el applet usando estas librerías, en concreto FEST Swing para simular la navegación e interacción con el applet.
  • log – Mini librería de log. No usamos ninguna de las habituales (Log4j, …) ya que como el applet viaja por Internet hasta el navegador del usuario, queremos que ocupe lo mínimo imprescindible, así que intentaremos evitar a toda costa las librerías de terceros.
  • src/ – algunos ficheros de configuración para generar los certificados.
  • generate-demo-certs.sh – script que usaremos para generar los certificados necesarios para firmar el applet.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15’’ (2.3 GHz Intel i7, 16GB 1600 Mhz DDR3, 500GB Flash Storage).
  • NVIDIA GeForce G7 750M
  • Sistema Operativo: Mac OS X Lion 10.9.4
  • OpenSSL 0.9.8y
  • Node v0.10.26
  • Firefox 31.0
  • Java Virtual Machine (JVM) 1.8.0_11
  • FEST Swing 1.2.1
  • SeleniumHQ 2.41.0

3. Generando los certificados para firmar el Applet

Para que un applet se pueda ejecutar en el navegador del usuario, aquél debe estar firmado. Esto es necesario para que navegador pueda identificar al creador del applet y así comprobar que es de confianza. Si este certificado viene expedido por una CA (Certificate Authority – Autoridad Certificadora) reconocida por el navegador, este permitirá la ejecución del applet. Sin embargo si la CA no es reconocida por el navegador, este mostrará un mensaje de alerta al usuario para que acepte explícitamente la ejecución del applet, ya que el navegador no confiará en él.

Para firmar el applet tenemos dos opciones:

  1. Usar un certificado expedido por una CA reconocida, como GoDaddy, VeriSign, Thawte, …
  2. Generar nosotros nuestra propia CA y añadirla al navegador para que la reconozca.

Para un entorno de producción evidentemente la opción recomendada es la 1., principalmente por los siguientes motivos:

  • Sobre todo porque si no se reconoce la CA, tal como hemos comentado antes, el navegador mostrará al usuario una alerta indicando que no puede confiar en el applet ya que no se reconoce la firma. Si el usuario no es experimentado esto puede causar rechazo o desconfianza en nuestra aplicación, y si el usuario no acepta el aviso del navegador nuestra aplicación no se ejecutará correctamente.
  • Así no tenemos que tocar la configuración del navegador de nuestros usuarios. Cosa que además en muchas ocasiones ni siquiera es posible (en una intranet podemos tener acceso a la configuración de todos los clientes, pero en una aplicación destinada a Internet esto es impensable).

Pero para un entorno de desarrollo, es perfectamente válida la opción 2., incluso recomendable ya que podemos simular pruebas, por ejemplo que el certificado está caducado.

Aquí vamos a ver como utilizar la opción 2. y generar nuestra propia CA y nuestros propios certificados.

  1. Vamos a crear un directorio para trabajar con los certificados.
  2. Ahora preparamos nuestra propia CA:

    Aquí destacamos como con el primer comando opensslestamos creando la clave de la CA y en el segundo comando openssl estamos creando el certificado propio de la CA. Recordad que un certificado tiene dos claves, la privada que nunca se debe dar, y la pública que es la que mandaremos al resto del mundo para que se comuniquen con nosotros.

    Con esto queda preparada la CA.

  3. Ahora vamos a importar la clave pública de la CA en el repositorio de claves de nuestra JVM para que está confíe en esta CA.

    Se supone que la clave pública nos la habrá mandado la CA por correo o la habremos conseguido por cualquier otro medio. En nuestro caso estamos accediendo directamente al fichero que hemos generado en el paso anterior, pero esto no tendría mucho sentido en la vida real.

    En este punto también debemos añadir la clave pública de la CA a la JVM que ejecutará el applet. Esto es imprescindible para que el navegador permita la ejecución del applet. Para ello hacemos:

    Ojo aquí porque el valor de la variable $BROWSER_CACERTS va a depender mucho del Sistema Operativo donde estemos. En el ejemplo que os pongo sería el sitio típico en OS X.

  4. Ahora vamos a generar un par de certificados nuestros. Estos certificados son los que usaremos para nuestras cosas, como por ejemplo firmar nuestro applet.

    Por ahora sólo son certificados autofirmados (por ahora no tienen ninguna relación con la CA). Y los hemos generado con la propia herramienta keytool de Java.

  5. Generamos una petición de firma para enviar a la CA. Es decir, vamos a preparar un fichero (demoCert1.csr) que mandaremos a la CA para que esta nos firme, y así valide, nuestro certificado.

  6. Ahora la CA firma la petición que hemos generado en el paso anterior.

    La CA nos devuelve el certificado firmado (el fichero demoCert1.pem.cer)

  7. Una vez recibimos el certificado firmado por la CA sólo nos queda importarlo en nuestro almacén de claves, para así poder usarlo para firmar nuestro applet.

Para que todo el proceso quede un poco más claro, presentamos un gráfico donde se ve como interactuamos con la CA, y así se vea quién hace qué.

Generadno el certificado para el applet
Generadno el certificado para el applet

Además en el script generate-demo-certs.sh tenéis toda la secuencia de pasos para reproducirla de forma sencilla en vuestro entorno.

 

4. Creando un Applet de ejemplo

Ahora vamos a crear un sencillo applet de ejemplo. El applet tendrá un campo de entrada, un botón y campo de salida, de forma que al dar al botón todo el texto que se encuentre en el campo de entrada se añadirá en el campos de salida.

Algo de este estilo.

Applet sencillo
Applet sencillo

El código lo encontramos en el fichero demo/applet/src/main/java/com/autentia/applet/DemoApplet.java

Como se puede ver es un applet normal y corriente, cabe destacar el método createGUI() donde se crean los componentes Swing del applet, y la particularidad de como le damos un nombre a cada componente para luego poder localizarlo. Esta sería la única condición que debe cumplir el applet que queremos probar.

 

5. Tests unitarios con FEST

Ya dijimos que FEST es una librería que nos ayuda a hacer tests automáticos. En el mismo proyecto donde tenemos el applet de ejemplo podemos encontrar un sencillo test:demo/applet/src/test/java/com/autentia/applet/DemoAppletTest.java

Aquí vemos como en la línea 14 se crea el “driver” (la clase FrameFixture), que luego se usará para interactuar con el applet y simular su manejo. Se van localizando los componente por el nombre que les dimos al crear el applet, y una vez localizado el componente se interactúa con él.

¡OJO! Estos tests aunque son ejecutados automáticamente por JUnit, son test que se están ejecutando fuera del navegador y por lo tanto sin ninguna restricción de seguridad. Esto quiere decir que son buenos para comprobar el funcionamiento interno del applet, pero hay que tener cuidado porque una vez se ejecute dentro del sandbox del navegador y se le apliquen las restricciones de seguridad, puede que el comportamiento cambie.

Teniendo en cuenta este aviso, vamos a seguir avanzando en el tutorial para ver como si que podemos ejecutar los tests dentro de un navegador.

 

6. Tests de sistema

A estos tests los hemos denominado de sistema porque vamos a levantar todo el sistema completo para probarlos, esto es:

  • Un Servidor Web implementado con Node.js
  • Un FireFox que hará una petición al Servidor Web, y este le servirá una página con el applet que queremos probar.
  • Todo orquestado con SeleniumHQ que será el encargado de manejar el navegador y lanzar los tests contra el applet usando JavaScript y LiveConnect (LiveConnect nos permite llamar desde JavaScript a Java y viceversa).

Para que el applet que hemos desarrollado en los puntos anteriores pueda interactuar con LiveConnect no nos vale tal cual, tenemos que, de alguna manera, exponer el driver que simulará la interacción con el usuario (igual que veíamos en los tests unitarios). Para ello vamos a extender el applet que ya tenemos, de forma que toda la “parafernalia” que necesitamos para los tests estará en otra clase sin ensuciar nuestro applet original. Además esta clase como es sólo para ejecutar los tests, nunca irá al código de producción.

Para hacer esta extensión vamos a ver dos métodos:

  1. Mediante el uso de FEST.
  2. Creando nosotros nuestra propia librería para probar applets: DRIA (Driven Rich Internet Application)

La pregunta que puede surgir ahora mismo, es ¿por qué hacer nuestra propia librería si ya tenemos FEST? Podríamos decir que la documentación de FEST no es todo lo buena que nos gustaría, y además hay ciertas piezas de FEST para probar applets que han quitado en las últimas versiones. Esto unido a los cambios en la JVM en cuanto a seguridad y applets (ver URLs de referencia), hace que la integración con FEST no resulte demasiado sencilla.

Desarrollado nuestra propia librería conseguimos: sencillez (no necesitamos toda la potencia de FEST) y flexibilidad (podemos hacer lo que necesitemos).

Cualquiera de las dos opciones, FEST o DRIA, tiene ventajas e inconvenientes y dependerá de cada caso usar una y otra. Aquí vamos a ver un ejemplo con cada una de ellas para que os sirva de guía.

 

6.1. Extendiendo nuestro applet con FEST

FEST en versiones anteriores tenía una clases para probar applets pero estas parece que han desaparecido en las últimas versiones, así que vamos a hacer a mano nuestro propio adaptador.

La clase la podéis encontrar en fest/src/main/java/com/autentia/fest/AppletFixtureFactory.java

Lo más importante de está clase está en la línea 14 donde se crear un “driver” para el applet que se ha pasado en el constructor.

En los ficheros de demo podemos encontrar un ejemplo de uso en el fichero demo/applet-fest/src/main/java/com/autentia/applet/fest/FestDrivenDemoApplet.java

Aquí podemos destacar como en la línea 1 extendemos nuestro applet y en la línea 8 usamos la clase anterior para crear el “driver”. Este driver lo localizaremos desde JavaScript, por LiveConnect, gracias al método getDriver() de la línea 11.

 

6.2. Extendiendo nuestro applet con DRIA

El uso de la librería que hemos creado a mano (DRIA) es muy similar a lo que hemos visto en el punto anterior.

Tenemos un adaptador que podemos encontrar en la clase dria/dria-swing/src/main/java/com/autentia/dria/swing/SwingDriver.java. Esta clase es algo más larga, y sería el equivalente a la clase FrameFixture de FEST.

La clase se limita a recibir un contenedor de Swing y recorrerlo recursivamente registrando todos sus componentes por nombre.

Al igual que con FEST en la carpeta demo podemos encontrar un ejemplo de uso de esta clase en el ficherodemo/applet-dria/src/main/java/com/autentia/applet/dria/DriaDrivenDemoApplet.java

Vemos que igual que antes estamos extendiendo nuestro applet, y que también exponemos el “driver” mediante el método getDriver() de la línea 12.

La ventaja en este caso es que el código queda más sencillo.

 

6.3. Interactuando con el applet mediante JavaScript gracias a LiveConnect

LiveConnect es una característica que implementan los navegadores y que nos va a permitir comunicar JavaScript con Java y viceversa. De esta forma desde un test con SeleniumHQ invocaremos un JavaScript que está en el navegador, y este JavaScript se comunicará mediante LiveConnect con el applet para simular la interacción, recoger los resultados y volver a mandarlos al test de SeleniumHQ para analizarlos.

El siguiente diagrama muestra el proceso completo.

LiveConnect
LiveConnect

Una vez entendemos el mecanismo, queda claro que necesitamos una pieza de JavaScript que hará de puente entre los tests y el applet. En este punto no he encontrado ninguna librería que funcionara (hay algo asociado con FEST, pero la verdad es que no he sido capaz de hacerlo funcionar), así que he desarrollado la mía propia.

Esta librería se encarga de generar el JavaScript de forma dinámica mientras ejecutamos los tests y luego se lo pasa a SeleniumHQ para que lo ejecute en el navegador.

Podéis encontrar el código en la carpeta dria/dria-applet/src/main/java/com/autentia/dria/applet. Y por ahora son simplemente cuatro clases, una por cada tipo de componente del applet con el que interactuamos:

  • JsAppletButton
  • JsAppletComponent
  • JsAppletDriver
  • JsAppletTextBox

Pongo el código de una a modo de ejemplo:

Se ve como tenemos un objeto js donde vamos añadiendo el JavaScript necesario para interactuar con el componente.

 

6.4. Firmado del applet

Ya tenemos todo el código listo, ahora es necesario empaquetar todas las clases del applet en un .jar y firmarlo con nuestro certificado para que el navegador lo reconozca correctamente.

Como hemos desarrollado dos formas para probar el applet FEST y DRIA, vamos a generar dos .jar distintos, de forma que podamos desplegar cada uno de forma independiente.

Vamos a ver el fichero de Gradle que se encarga de esta tarea para el applet DRIA (la parte de FEST sería análoga).

El fichero lo encontramos en demo/applet-dria/build.gradle.

Cosas importantes:

  • línea 8 – generamos un sólo .jar que contendrá todas las clases, tanto nuestras, como las de librerías de terceros de las que dependemos. Así solo desplegaremos un .jar y el navegador sólo se tendrá que descargar un único.jar.
  • línea 9 – definimos los atributos del fichero META-INF/MANIFEST.MF. Son especialmente importantes:
    • Permissions – Indica si el applet se puede ejecutar en un sandbox (sería la opción más recomendable ya que al estar más restringida da menos problemas), o si necesita all-permissions.
    • Codebase – Dominio de donde se tiene que descargar el applet. Así evitamos que el applet pueda ser desplegado en otro dominio en algún intento de suplantación.
    • Application-Library-Allowable-Codebase – indica en que dominios pueden estar la página HTML que usa el applet o el fichero JNLP. Si el applet está en la misma localización que el HTML o el JNLP no haría falta especificar este atributo, pero es recomendable para evitar posibles intentos de suplantación.
    • Caller-Allowable-Codebase – Lista de dominios que pueden pueden hacer llamadas de LiveConnect. El JavaScript descargado de los dominios que están en esta lista podrán hacer llamadas al applet a través de LiveConnect.
  • línea 21 – es donde firmamos el applet indicando el almacén de certificados y el alias del certificado que queremos usar.

 

6.5. Creando el servidor Web

El siguiente paso para lanzar los tests de sistema es crear un Servidor Web que nos devuelva el HTML y los recursos, incluido el .jar de nuestro applet. Para ello vamos a usar Node.js ya que es realmente sencillo y rápido.

Para crear el servidor sólo necesitamos el fichero demo/system-test/server.js

Como véis este fichero tiene dos sencillas líneas, y lo que hace es servir por el puerto 8080 los ficheros que se encuentran en src/main/webapp.

Para arrancar el servidor basta con ejecutar en la línea de comando:

Y ya podremos hacer peticiones a nuestro servidor, como http://localhost:8080/system-test.html, y deberíamos ver algo como:

Pagina para la prueba del applet
Pagina para la prueba del applet

 

6.6. Ejecutando los tests de sistema

Creo que ya lo tenemos todo listo, así que el último paso es lanzar los tests. Vemos como el código de los tests de sistema:

Cosas importantes:

  • línea 8 – estamos creando un perfil de FireFox configurando la propiedad plugin.state.java para que el FireFox no nos pregunte si queremos permitir la ejecución del applet.
    Esto no tiene nada que ver con los certificados, simplemente desde algún tiempo casi todos los navegadores tomaron la decisión de añadir este control como medida de seguridad, de forma que siempre que hay una RIA (Applet, Flash, …) piden permiso al usuario para ejecutarla. Evidentemente esto sería un inconveniente para un test automático que se tiene que ejecutar sin intervención de un operador, así que hacemos este “truco”.

    Ojo, porque esto dependerá del navegador que estemos usando para el test, en nuestro caso FireFox.

  • línea 25 – vemos como vamos a lanzar el mismo test para los dos applets: driaApplet y festApplet.
  • línea 32 – estamos usando la librería que hemos creado para generar el JavaScript y comunicarnos con el applet mediante LiveConnect. Vemos como creamos una instancia de esta librería pasándole el ejecutor de JavaScript que nos proporciona SeleniumHQ y el nombre del applet que vamos a querer probar.
  • línea 36 – es un test normal y corriente, como los que veíamos en el apartado de los test unitarios. Lo bueno de este test es justamente eso, que no sabe si está ejecutando el test contra un applet en un navegador o de forma aislada, así que podría ser reutilizable para hacer las dos cosas.

 

7. Conclusiones

Se que el tutorial ha quedado bastante denso, pero el tema no es para nada sencillo e intervienen muchas piezas distintas. De hecho el tutorial no es más que una guía para ayudaos a navegar y comprender el código que está en el repositorio, pero es el código que hay en el repositorio el que debéis estudiar para ver todos los detalles. Os animo a que os lo bajéis lo probéis, lo modifiquéis, y en general juguéis con él para hacerlo vuestro.

Además hay que tener en cuenta que, como cualquier test de sistema, es muy frágil, ya que intervienen muchas piezas distintas que tienen que estar perfectamente orquestadas. En cuanto cambie la versión de máquina virtual, el navegador, el sistema operativo, … puede ser que el test falle.

Por eso la idea de este tipo de tests no es probar absolutamente toda la funcionalidad, sino simplemente un caso de uso crítico o alguna característica concreta. En el caso del applet el test de sistema sirve para comprobar cómo se comporta ante las restricciones de seguridad impuestas por la JVM y el navegador.

Teniendo esto último en cuenta, este test podría derivar en un conjunto de tests distribuidos que se ejecutan en distintas máquinas virtuales con distintos entornos de sistema operativo, JVM y navegador, de forma que automáticamente fuéramos capaces de probar todos los entornos que consideramos críticos. Pero eso lo dejamos para otro tutorial ;P

 

8. Sobre el autor

Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia (Desarrollo de software, Consultoría, Formación)

mailto:alejandropg@autentia.com

Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

http://www.autentia.com

Dejar respuesta

Please enter your comment!
Please enter your name here