Un sito web che si rispetti, offre sempre ai suoi utenti una navigazione semplice e intuitiva.
Ma quanto è facile riuscire a raggiungere questo obbiettivo? E soprattutto, per quanto interessa a noi, quanto è facile essere sicuri che tutto funzioni senza intoppi ogni volta che viene rilasciata una nuova versione del sito?
Proprio in questo secondo punto ci viene incontro Selenium, un ottimo tool per registrare le vostre azioni di navigazione e convertirle in un test di integrazione eseguibile ogni volta che viene rilasciata una nuova versione del vostro sito.
In questo primo articolo vedremo come installare un Grid per rendere il test ancora più approfondito e performante.
Perchè “Grid”?
L’esecuzione “classica” di selenium prevede che i test vengano eseguiti sul vostro PC, sfruttando quindi i browser installati su di esso, questo comporta alcuni side-effect piuttosto sconvenienti.
- L’esecuzione dei test risulterà estremamente pesante in quanto l’intero consumo di risorse sarà concentrato sulla stessa macchina
- Dovrete installare diversi browser (Firefox/Chrome/IE/Opera/etc..) per avere una buona copertura di test case
- In ogni caso la copertura di test sarà limitata all’architettura del vostro PC ( Windows / Linux / Mac )
- Se, come tutti, usate lo stesso PC anche per la navigazione quotidiana è molto probabile che almeno un browser sia “infestato” da diversi plugin che potrebbero condizionare l’esito e la correttezza dei test eseguiti.
Nell’esecuzione a “Grid” invece, selenium istanzia un Hub, ovvero un nodo di smistamento delle richieste.
Su questo Hub si registreranno invece diversi Nodi, ciascuno dei quali mette a disposizione delle Capabilities, che altro non sono che dei browser sui cui verranno eseguiti i test.
I nodi possono essere installati su macchine diverse, con architetture diverse, e con browser diversi. E’ anche possibile installare piu nodi sulla stessa macchina, ciascuno con delle Capabilities differenti e con diversi parallelismi.
Immaginate quindi di avere questa situazione ideale
- Hub di selenium, incaricato di instradare le richieste
- Nodo Linux su cui sono installati i browser Firefox, Chrome e Opera
- Nodo Windows su cui sono installati i browser InternetExplorer, Firefox, Chrome, Opera e Safari
- Nodo Mac su cui sono installati i browser Firefox, Chrome, Opera e Safari
In questo modo voi potrete semplicemente inviare i test (dalla vostra IDE preferita o da un qualsiasi processo di build automatico come maven) all’hub, il quale verificherà quali sono le Capability richieste dai vostri test, le confronterà con i nodi a sua disposizione e instraderà il test al nodo più adatto ad eseguirlo.
quali sono i vantaggi di tutto questo?
- La vostra macchina si alleggerisce di tutto il carico di esecuzione dei test veri e propri, delegandola all’hub (che a sua volta delegherà ai singoli nodi)
- La copertura dei test aumenta enormemente (a patto di avere ovviamente un parco macchine adeguato) permettendo di coprire in un solo colpo più browser e più sistemi operativi. Se non avete problemi di budget si può pensare anche a diversificare le versioni dei diversi browser o dei sistemi operativi, tutto dipende da quanto è importante per voi avere un’efficace copertura di test.
- Tutti i test sono lanciati su ambienti “puliti” e dedicati, sui quali potete installare solo i plugin effettivamente necessari al testing.
- Se correttamente scritti, i test possono essere parallelizzati, distribuendo il carico su più nodi e riducendo drasticamente il tempo di esecuzione. Le stesse capability possono anche essere ridondate su piu nodi per aumentare il parallelismo)
Hands On
Ok, iniziamo la nostra installazione.
Per velocizzare il tutto decidiamo che l’Hub e il Nodi linux gireranno sulla stessa macchina, vi consiglio di crearvi un’istanza munita di Desktop grafico e connessione Remote desktop o VNC (appena avrò tempo magari scriverò un post anche su questo).
Installate i browser che desiderate utilizzare e una JRE java (non credo ci siano limitazioni se oracle o openjdk, facciamo sempre riferimento alla prima)
I browser potrebbero effettivamente girare anche solo sfruttando l’X Virtual Framebuffer ma avere un desktop reale vi semplificherà parecchio la vita e vi permetterà di monitorare l’effettiva corretta esecuzione dei test all’interno dei browser.
Installazione dell’Hub
Decidete il percorso dove volete installare il tutto e createvi un minimo di struttura di folder, qui supponiamo di operare nel folder /opt/selenium:
- /opt/selenium
- bin (qui metteremo i nostri script di startup)
- lib (cartella per le librerie di selenium)
- logs (what else? )
- drivers (driver aggiuntivi necessari per alcuni browser)
Ora scaricatevi l’ultima versione del selenium-server-standalone.jar dal progetto selenium su googlecode e copiate il jar sotto il folder lib.
A questo punto il comando da lanciare per inizializzare l’hub è semplicissimo:
java -jar selenium-server-standalone-X.Y.Z.jar -role hub -port 4444
una volta partito, collegatevi via browser all’indirizzo della macchina stessa sulla porta in questione
e dovreste vedere l’home page dell’hub… vuota. Non preoccupatevi, è tutto corretto, significa semplicemente che l’hub è pronto a ricevere connessioni, ma nessun nodo si è ancora registrato.
Prima però di passare all’installazione dei nodi facciamo un po di ordine.
Infatti finora abbiamo lanciato il comando a mano, e appena usciremo dalla shell il processo morirà impietosamente. Inoltre vogliamo utilizzare quella bellissima struttura di folder che ci siamo preparati, avere dei log con lo storico di quel che succede e poter avviare e stoppare l’hub a piacimento.
Ehi, sembrano tutti i problemi trattati nel post Start, Stop, Restart!
basta qualche modifica ed ecco un nuovo script per gestire il nostro Hub:
#!/bin/bash
#
# Startup script for Selenium Hub
# Description: This shell script takes care of starting and stopping Selenium Hub
# chkconfig: – 80 20
#
## Source function library.
#. /etc/rc.d/init.d/functions
##JAVA_HOME=/usr/lib/jvm/jdk1.6.0_33
JAVA_EXE=$JAVA_HOME/bin/javaSERVICE_NAME=selenium_hub
SERVICE_PATH=/opt/selenium
SERVICE_PORT=4444
export DISPLAY=selenium:0LIB_PATH=$SERVICE_PATH/lib
LOG_PATH=$SERVICE_PATH/logs
SELENIUM=selenium-server-standalone-2.34.0.jar
DOWNLOAD_SELENIUM=https://selenium.googlecode.com/files/$SELENIUMSERVICE_PID_FILE=$SERVICE_PATH/$SERVICE_NAME.pid
SCREEN_FILE=$SERVICE_PATH/$SERVICE_NAME.screenRUN=”$JAVA_EXE -jar $LIB_PATH/$SELENIUM -role hub -port $SERVICE_PORT”
RUNAS_USER=selenium
RUN_AS_GROUP=selenium
RUN_USER=$(whoami)
SHUTDOWN_WAIT=20
BOOTSTRAP_CLASS=selenium-server-standaloneEXTRA_STOP_COMMAND=””
EXTRA_START_COMMAND=””
EXTRA_RESTART_COMMAND=””service_pid() {
echo `ps aux | grep $BOOTSTRAP_CLASS|grep hub| grep $SERVICE_PORT| grep $SERVICE_PATH | grep -v grep | awk ‘{ print $2 }’`
}start(){
pid=$(service_pid)if [ ! -d “$LIB_PATH” ]; then
echo “Creating unexisting $LIB_PATH”
mkdir -p $LIB_PATH
chown -R $RUN_AS_USER:$RUN_AS_GROUP $LIB_PATH
fiif [ ! -f “$LIB_PATH/$SELENIUM” ]; then
echo “Downloading $SELENIUM”
wget $DOWNLOAD_SELENIUM -P $LIB_PATH
fiif [ ! -d “$LOG_PATH” ]; then
echo “Creating unexisting $LOG_PATH”
mkdir -p $LOG_PATH
chown -R $RUN_AS_USER:$RUN_AS_GROUP $LOG_PATHfi
if [ -n “$pid” ]
then
echo “$SERVICE_NAME is already running (pid: $pid)”
else
# Start service
echo “Starting $SERVICE_NAME”
if [ “$RUN_USER” == “$RUNAS_USER” ]; then
echo
echo “Running with current user”
if [ “$EXTRA_START_COMMAND” != “” ]; then
echo
echo “Running EXTRA_START_COMMAND”
“$EXTRA_START_COMMAND”
fi
$RUN > $LOG_PATH/$SERVICE_NAME.log 2>&1 &else
echo “Running with sudo”
if [ “$EXTRA_START_COMMAND” != “” ]; then
echo
echo “Running EXTRA_START_COMMAND”
sudo -u $RUNAS_USER sh “$EXTRA_START_COMMAND”
fi
sudo -u $RUNAS_USER sh $RUN > $LOG_PATH/$SERVICE_NAME.log 2>&1 &
fi
fireturn 0
}stop() {
pid=$(service_pid)
if [ -n “$pid” ]
then
echo “Stoping $SERVICE_NAME”
kill $pidlet kwait=$SHUTDOWN_WAIT
count=0;
until [ `ps -p $pid | grep -c $pid` = ‘0’ ] || [ $count -gt $kwait ]
do
echo -n -e “\nwaiting for processes to exit”;
sleep 1
let count=$count+1;
doneif [ $count -gt $kwait ]; then
echo -n -e “\nkilling processes which didn’t stop after $SHUTDOWN_WAIT seconds”
kill -9 $pid
fi
else
echo “$SERVICE_NAME is not running”
fi
if [ “$RUN_USER” == “$RUNAS_USER” ]
then
if [ “$EXTRA_STOP_COMMAND” != “” ]; then
echo “Running EXTRA_STOP_COMMAND”
“$EXTRA_STOP_COMMAND”
fi
else
if [ “$EXTRA_STOP_COMMAND” != “” ]; then
echo “Running EXTRA_STOP_COMMAND”
sudo -u $RUNAS_USER sh “$EXTRA_STOP_COMMAND”
fi
fi
return 0
}case $1 in
start)
start
;;stop)
stop
;;
restart)
stop
echo
echo “Sleeping 5s before starting again”
sleep 5
if [ “$RUN_USER” == “$RUNAS_USER” ]
then
if [ “$EXTRA_RESTART_COMMAND” != “” ]; then
echo “Running EXTRA_RESTART_COMMAND”
“$EXTRA_RESTART_COMMAND”
fi
else
if [ “$EXTRA_RESTART_COMMAND” != “” ]; then
echo “Running EXTRA_RESTART_COMMAND”
sudo -u $RUNAS_USER sh “$EXTRA_RESTART_COMMAND”
fi
fi
start
;;
status)
pid=$(service_pid)
if [ -n “$pid” ]
then
echo “$SERVICE_NAME is running with pid: $pid”
else
echo “$SERVICE_NAME is not running”
fi
;;
*)
echo “Usage: $0 {start|restart|stop|status}”
exit 1
esac
exit 0
Et Voilà…
Visto che siamo dei gran pigroni e non vogliamo dimenticarci nulla ho aggiunto anche un paio di controlli aggiuntivi, quindi qualora i folder logs e lib non fossero presenti lo script si premurerà di crearli, e qualora mancasse la libreria di selenium-server-standalone, lo script lo scaricherà per voi, molto comodo per fare un upgrade veloce 😉
Ora il nostro Hub può essere installato come servizio con i classici comandi start/stop/restart, logga correttamente e fa tutto quello che un sano script di startup gli consente di fare.
Lanciamo il nostro nuovo script con un bel ./selenium_hub.sh start e controlliamo che la pagina dell’hub sia ancora correttettamente al suo posto. C’è? Allora possiamo iniziare a popolarla di utilissimi nodi.
Installazione dei nodi
Come abbiamo anticipato è possibile installare più di un nodo su una singola macchina e mixare i browser come più ci aggrada.
Diciamo che vogliamo puntare su un buon parallelismo nei test quindi creeremo un nodo dedicato per ciascun browser disponibile sulla macchina, ciascuno con una capacità di 10 istanze parallele.
Iniziamo dal caso piu semplice, firefox. Il comando che ci serve per far partire il nodo e farlo registrare sul nostro hub è il seguente:
java -jar selenium-server-standalone-X.Y.Z.jar -role node -port 5555 -maxSession 10 -hub http://192.168.1.100:4444/grid/register -browser “browserName=firefox,platform=LINUX,maxInstances=10”
Come potete notare la libreria usata è esattamente la stessa ma stavolta il “ruolo” è quello di node anziché di hub, mentre dovremo fornire come riferimento l’url/porta dell’hub su cui il nodo dovrà registrarsi.
Il tipo di browser che vogliamo utilizzare è definito dalla stringa che segue il parametro browser e dichiara che questo nodo può servire tutte le capability che richiedano il browser firefox e la piattaforma linux.
Infine il parallelismo è dato da due parametri, il maxInstances del browser , che specifica quante istanze di browser mettere a disposizione, e il parametro maxSession globale, che stabilisce quante sessioni il nodo può gestire in parallelo
Lanciato il comando e atteso qualche secondo per la registrazione del nodo, dovreste vederlo comparire nella nostra pagina dell’Hub con tante piccole icone di firefox quanto sono le istanze che avete configurato:
ok, anche in questo caso creiamoci lo script di Start, Stop, Restart per gestire il nostro processo, qui trovate l’equivalente di quello già fatto per il server:
#!/bin/bash
#
# Startup script for Selenium Hub
# Description: This shell script takes care of starting and stopping Selenium Hub
# chkconfig: – 80 20
#
## Source function library.
#. /etc/rc.d/init.d/functions
##
JAVA_HOME=/usr/lib/jvm/jdk1.6.0_33
JAVA_EXE=$JAVA_HOME/bin/java
SERVICE_NAME=selenium_node_firefox
SERVICE_PATH=/opt/selenium
SERVICE_PORT=4444
NODE_PORT=5555
NODE_BROWSER=”browserName=firefox,platform=LINUX,maxInstances=10″
NODE_DRIVER=””
export DISPLAY=selenium:0
LIB_PATH=$SERVICE_PATH/lib
LOG_PATH=$SERVICE_PATH/logs
SELENIUM=selenium-server-standalone-2.34.0.jar
DOWNLOAD_SELENIUM=https://selenium.googlecode.com/files/$SELENIUM
SERVICE_PID_FILE=$SERVICE_PATH/$SERVICE_NAME.pid
SCREEN_FILE=$SERVICE_PATH/$SERVICE_NAME.screen
RUN=”$JAVA_EXE -jar $LIB_PATH/$SELENIUM -role node -maxSession 10 -port $NODE_PORT http://localhost:$SERVICE_PORT/grid/register -browser $NODE_BROWSER $NODE_DRIVER”
RUNAS_USER=selenium
RUN_AS_GROUP=selenium
RUN_USER=$(whoami)
SHUTDOWN_WAIT=20
BOOTSTRAP_CLASS=selenium-server-standalone
EXTRA_STOP_COMMAND=””
EXTRA_START_COMMAND=””
EXTRA_RESTART_COMMAND=””
service_pid() {
echo `ps aux | grep $BOOTSTRAP_CLASS|grep node| grep $NODE_PORT| grep $SERVICE_PATH | grep -v grep | awk ‘{ print $2 }’`
}
start()
{
pid=$(service_pid)
if [ ! -d “$LIB_PATH” ]; then
echo “Creating unexisting $LIB_PATH”
mkdir -p $LIB_PATH
chown -R $RUN_AS_USER:$RUN_AS_GROUP $LIB_PATH
fi
if [ ! -f “$LIB_PATH/$SELENIUM” ]; then
echo “Downloading $SELENIUM”
wget $DOWNLOAD_SELENIUM -P $LIB_PATH
fi
if [ ! -d “$LOG_PATH” ]; then
echo “Creating unexisting $LOG_PATH”
mkdir -p $LOG_PATH
chown -R $RUN_AS_USER:$RUN_AS_GROUP $LOG_PATH
fi
if [ -n “$pid” ]
then
echo “$SERVICE_NAME is already running (pid: $pid)”
else
# Start service
echo “Starting $SERVICE_NAME”
if [ “$RUN_USER” == “$RUNAS_USER” ]
then
echo “Running with current user”
if [ “$EXTRA_START_COMMAND” != “” ]; then
echo “Running EXTRA_START_COMMAND”
“$EXTRA_START_COMMAND”
fi
$RUN > $LOG_PATH/$SERVICE_NAME.log 2>&1 &
else
echo “Running with sudo”
if [ “$EXTRA_START_COMMAND” != “” ]; then
echo “Running EXTRA_START_COMMAND”
sudo -u $RUNAS_USER sh “$EXTRA_START_COMMAND”
fi
sudo -u $RUNAS_USER sh $RUN > $LOG_PATH/$SERVICE_NAME.log 2>&1 &
fi
fi
return 0
}
stop() {
pid=$(service_pid)
if [ -n “$pid” ]
then
echo “Stoping $SERVICE_NAME”
kill $pid
let kwait=$SHUTDOWN_WAIT
count=0;
until [ `ps -p $pid | grep -c $pid` = ‘0’ ] || [ $count -gt $kwait ]
do
echo -n -e “\nwaiting for processes to exit”;
sleep 1
let count=$count+1;
done
if [ $count -gt $kwait ]; then
echo -n -e “\nkilling processes which didn’t stop after $SHUTDOWN_WAIT seconds”
kill -9 $pid
fi
else
echo “$SERVICE_NAME is not running”
fi
if [ “$RUN_USER” == “$RUNAS_USER” ]
then
if [ “$EXTRA_STOP_COMMAND” != “” ]; then
echo “Running EXTRA_STOP_COMMAND”
“$EXTRA_STOP_COMMAND”
fi
else
if [ “$EXTRA_STOP_COMMAND” != “” ]; then
echo “Running EXTRA_STOP_COMMAND”
sudo -u $RUNAS_USER sh “$EXTRA_STOP_COMMAND”
fi
fi
return 0
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
echo
echo “Sleeping 5s before starting again”
sleep 5
if [ “$RUN_USER” == “$RUNAS_USER” ]
then
if [ “$EXTRA_RESTART_COMMAND” != “” ]; then
echo “Running EXTRA_RESTART_COMMAND”
“$EXTRA_RESTART_COMMAND”
fi
else
if [ “$EXTRA_RESTART_COMMAND” != “” ]; then
echo “Running EXTRA_RESTART_COMMAND”
sudo -u $RUNAS_USER sh “$EXTRA_RESTART_COMMAND”
fi
fi
start
;;
status)
pid=$(service_pid)
if [ -n “$pid” ]
then
echo “$SERVICE_NAME is running with pid: $pid”
else
echo “$SERVICE_NAME is not running”
fi
;;
*)
echo “Usage: $0 {start|restart|stop|status}”
exit 1
esac
exit 0
ok, e una è fatta, passiamo a chrome.
Qui il discorso è leggermente piu complicato (ma non di molto) perché come vedrete chrome necessita di un driver aggiuntivo per funzionare correttamente.
Scaricate i driver per chrome dal relativo altro progetto chromedriver, unzippate l’eseguibile e copiatelo nella cartella drivers.
ora tutto quello che dovete fare è invocare lo stesso comando usato in precedenza, ma con una piccola aggiunta:
java -jar selenium-server-standalone-X.Y.Z.jar -role node -port 5556 -maxSession 10 -hub http://192.168.0.10:4444/grid/register -browser browserName=chrome,platform=LINUX -Dwebdriver.chrome.driver=/opt/selenium/driver/chromedriver
Fate molta attenzione ad usare una porta differente da quella usata in precedenza per chrome, altrimenti il nodo non riuscirà a partire e registrarsi correttamente.
Lascio a voi la stesura dello scritp di startup, se controllate bene quello di firefox, vedrete che sono già predisposte sia la variabile della porta sia quella (vuota per firefox) per l’utilizzo di driver aggiuntivi.
Facciamo un veloce salto in avanti, supponiamo che abbiate configurato anche Opera (non servono driver aggiuntivi, quindi anche qui basta adattare lo script di firefox e sarete pronti, occhio sempre ad usare porte differenti)
Ecco come apparirà il vostro Hub una volta registrati tutti e tre i nodi:
Complimenti, avete completato l’installazione di un Selenium Grid Linux!
Nei prossimi articoli cercheremo di aggiungere anche i nodi per windows e di scrivere dei test che siano in grado di utilizzarli tutti in maniera modulare.