Entornos de Selenium Grid: Local vs Virtualizado

Selenium es una de las herramientas más usadas para la automatización y ejecución de pruebas end to end. Desde hace un tiempo SeleniumHQ permite la instanciación y conexión de máquinas en red mediante Selenium Grid. Esto nos posibilita gestionar una nube de máquinas y navegadores a través de un solo hub que coordina las peticiones y las enruta en función de las características definidas en la configuración de cada prueba.  

A continuación vamos a ver cómo configurar nuestro grid de Selenium y vamos a comparar la ejecución de pruebas en un entorno local con uno virtualizado mediante Docker.

Ejemplo de Selenium Grid en local

Nos ponemos manos a la obra con la configuración de nuestro entorno en local. Para ello necesitaremos tener instalados los navegadores web que vamos a configurar posteriormente en el grid. El resultado final, cuando tengamos todo nuestro entorno arriba, será el siguiente:

Lo que vemos es una máquina Linux, localhost, en la que tenemos corriendo dos scripts, uno con el hub y otro con un nodo de Selenium en el que se dispone de tres instancias de Firefox y otras tantas de Google Chrome. Además, disponemos de una máquina virtualizada con Windows 10 que levanta una instancia de Internet Explorer.

El fichero de configuración del hub es el siguiente:

#!/bin/bash
IP=`ifconfig eth0 | grep 'inet:' | cut -d: -f2 | awk '{ print $1}'`
java -jar selenium-server-standalone-3.0.1.jar -role hub -maxSession 50 -host $IP

El fichero de configuración del nodo en Linux del ejemplo es así:

#!/bin/bash
IP=`ifconfig eth0 | grep 'inet:' | cut -d: -f2 | awk '{ print $1}'`
java -Dwebdriver.gecko.driver=$HOME/Software/geckodriver -jar 
selenium-server-standalone-3.0.1.jar -role node -hub http://$IP:4444/grid/register 
-browser browserName=firefox,platform=LINUX,maxInstances=3 -browser 
browserName=chrome,platform=LINUX,maxInstances=3

El fichero de configuración del nodo en Windows del ejemplo es así:

java -jar selenium-server-standalone-3.0.1.jar -role node -hub 
http://$IP:4444/grid/register -browser browserName=iexplorer,platform=WIN8_1

Este entorno tiene una característica importante a la hora de ejecutar las pruebas. Cada ejecución que realicemos supondrá que se ejecute un navegador en el entorno, levantándolo y mostrándolo en pantalla.

Esto no es un problema si estamos depurando la prueba o desarrollando aplicando una metodología BDD o TDD, pero si lo que queremos es ejecutar un set de pruebas más o menos grande mientras realizamos otras tareas, lo vamos a tener complicado; la lucha por retener el foco del ratón contra Selenium va a ser épica. En ese caso lo que necesitamos es montar todo el grid de Selenium en un entorno virtual.

Selenium Grid en Docker

Una de las posibilidades para virtualizar todo el entorno es el uso de Docker gestionado mediante Docker Compose.

Orquestando el grid mediante Docker Compose

Lo primero que necesitaremos en el path desde el que vamos a ejecutar el Docker Compose es definir el yml de configuración. El fichero se debe llamar docker-compose.yml con el siguiente formato:

selhub:
  image: mydocker/selenium-hub
ports:
 - "4444:4444"

firefox:
  image: mydocker/selenium-node-firefox
  expose:
  - "5555"
  links:
  - selhub
extra_hosts:
  - "portalgrupo.cliente.pre:xxx.xxx.xxx.xxx"
  - "entorno.cliente.es:yyy.yyy.yyy.yyy"
chrome:
  image: mydocker/selenium-node-chrome
  volumes:
  - /dev/shm:/dev/shm
  expose:
  - "5555"
  links:
  - selhub
extra_hosts:
  - "portalgrupo.cliente.pre:xxx.xxx.xxx.xxx"
  - "entorno.cliente.es:yyy.yyy.yyy.yyy"

El parámetro extra_hosts nos permite modificar el fichero /etc/hosts del docker para, por ejemplo, indicar la IP de los entornos de nuestros clientes que sabemos que no se van a resolver a través del DNS.

En el parámetro links de cada nodo indicamos el docker que contiene el nodo, contra el que se tienen que conectar cuando se levante todo el entorno.

Al ejecutar docker-compose start se levantará todo el entorno, una vez hayamos generado todos los dockers, disponiendo de una nube de Selenium como la que se muestra en la siguiente captura:

Hub de Selenium

El siguiente paso es crear un dockerfile por cada tipo de nodo y por el hub que queremos desplegar. En nuestro caso son dos nodos diferentes, uno con dos instancias de Firefox y otro con dos instancias de Chrome. Como ejemplo así es como se configuraría todo el nodo del hub:
Primero necesitamos crear el script que levantará el hub de selenium, al que he llamado selenium-hub.sh y que no dista casi nada del ejemplo anterior:

#!/bin/bash
cd /opt && java -jar selenium-server-standalone.jar -role hub -maxSession 50 -timeout 
60

Por otro lado necesitaremos configurar supervisord mediante la creación de un fichero supervisord.conf:

[supervisord]
nodaemon=true

[program:selenium-hub]
command=/root/selenium-hub.sh

Descargamos y copiamos junto a nuestros ficheros el fichero .jar del Selenium Server y creamos el Dockerfile:

FROM ubuntu:14.04

RUN apt-get update && apt-get upgrade -y
RUN apt-get install software-properties-common -y
RUN add-apt-repository ppa:openjdk-r/ppa -y
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y openjdk-8-jre
RUN apt-get install -y supervisor

RUN mkdir -p /var/log/supervisor

ADD selenium-server-standalone.jar /opt/
ADD selenium-hub.sh /root/

RUN chmod +x /root/selenium-hub.sh

ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre

RUN echo "export VISIBLE=now" >> /etc/profile

RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ADD supervisord.conf /etc/supervisor/supervisord.conf

EXPOSE 4444

CMD ["/usr/bin/supervisord"]

Es el momento de generar el docker del hub ejecutando el siguiente comando:

docker build -t mydocker/selenium-hub .

Creamos los nodos

Como los dos tipos de nodo que vamos a generar tienen similitudes vamos a generar tres docker en vez de dos. El primero tiene el entorno básico: Selenium, openJDK, el servidor gráfico xvfb y supervisord. Los otros dos docker, que usarán a éste como base, sólo incluirán la instalación de los navegadores.

Así que creamos primero el supervisord.conf:

[supervisord]
nodaemon=true

[program:Xvfb]
command=/root/xvfb.sh

Y el fichero xvfb.sh que exportará el display. Así podremos hacer capturas de pantalla en la ejecución de las pruebas:

#!/bin/bash

Xvfb :1 -screen 1 1024x768x16

Y creamos el Dockerfile:

FROM ubuntu:14.04

RUN apt-get update && apt-get upgrade -y
RUN apt-get install software-properties-common -y
RUN add-apt-repository ppa:openjdk-r/ppa -y
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y openjdk-8-jre bzip2
RUN apt-get install -y supervisor xvfb wget

RUN mkdir -p /var/log/supervisor

ADD selenium-server-standalone.jar /opt/
ADD xvfb.sh /root/

ENV DISPLAY :1

ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre

RUN echo "export VISIBLE=now" >> /etc/profile

RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ADD supervisord.conf /etc/supervisor/supervisord.conf

CMD ["/usr/bin/supervisord"]

Generamos el docker ejecutando:

docker build -t mydocker/selenium-node .

Creamos el nodo de Firefox

Firefox, al igual que Chrome, requiere desde la versión 46 que instalemos un driver para la conexión con Selenium llamado Marionette. El resto de la generación del docker es muy similar a los casos anteriores.

Creamos el selenium-node.sh que ejecutará el nodo:

#!/bin/bash

cd /opt && java -jar selenium-server-standalone.jar -role node -hub 
http://$SELHUB_1_PORT_4444_TCP_ADDR:4444/grid/register -port 5555 -browser 
browserName=firefox,maxInstances=2,platform=LINUX

Como vemos usamos una variable de entorno que genera Docker-Compose $SELHUB_1_PORT_4444_TCP_ADDR que nos da, de forma dinámica, la dirección IP del nodo que referenciamos con el nombre selhub.

Configuramos el Dockerfile correspondiente:

FROM mydocker/selenium-node

RUN apt-get update
RUN apt-get install -y firefox

ADD env.sh /opt/
RUN chmod +x /opt/env.sh

RUN cd /tmp/ && wget https://github.com/mozilla/geckodriver/releases/download/v0.13.0/geckodriver-v0.13.0-linux64.tar.gz

RUN apt-get install -q -y libdbus-glib-1-2

RUN tar -zxvf /tmp/geckodriver* -C /opt/
ENV PATH $PATH:/opt

ADD selenium-node.sh /root/

RUN chmod +x /root/*.sh

RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ADD supervisord.conf /etc/supervisor/supervisord.conf

EXPOSE 5555

CMD ["/usr/bin/supervisord"]

Y generamos el contenedor:

docker build -t mydocker/selenium-node-firefox .

Ya, con todos los contenedores generados, podemos ejecutar el compose:

¿Local o virtualizado?

La elección dependerá de nuestras necesidades. El entorno en local nos va a ser útil durante las fases de desarrollo y refactorización del código dentro de un proceso de desarrollo BDD o TDD, pues nos permite ver la ejecución de la prueba.

En el caso de ejecutar un gran volumen de pruebas de forma periódica o programada, por ejemplo en el pipeline en Jenkins de despliegue de nuestro desarrollo, o si necesitamos disponer de un entorno compartido entre varios equipos de desarrollo, nos convendrá usar un entorno virtualizado.

A nivel de performance el entorno virtualizado es ligeramente más rápido que el entorno local, es fácilmente replicable en otros entornos y escalable horizontalmente. En este ejemplo hemos usado docker-compose, pero podemos gestionar este mismo entorno de pruebas en un sistema cloud como OpenShift o similares.

Gracias a usar selenium-grid, abstraemos el diseño de nuestras pruebas de la arquitectura sobre la que se ejecutan. Nos es indiferente, a la hora de configurar las pruebas contra el grid, cómo esté montado el grid al que enviamos las peticiones, con lo que podremos convivir con los dos sistemas en paralelo en nuestras instalaciones, teniendo, por ejemplo, un grid en local para el tiempo de desarrollo de pruebas y código y otro virtualizado para las pruebas regresivas, ejecuciones desde Jenkins, etc.

Técnico de procedimientos de calidad y automatización de pruebas. Entusiasta del software libre y de cacharrear con nuevas tecnologías. Mi tiempo libre es para mis mascotas, el monte y la música.

Ver toda la actividad de Jacobo Da Riva

Escribe un comentario