Headless Selenium testing environment on Centos 6.5

Selenium is a set of tools which allows to test web applications automatically. Here’s a way to get it working on Centos 6. At the end we should be able to run headless Selenium tests with Firefox. Please note that as X11 disaplay server we will use Xvfb.

At first let’s install required packages and prepare directories.

yum install firefox Xvfb libXfont Xorg
mkdir /usr/lib/selenium /var/log/selenium /var/log/Xvfb
chown screener.screener  /usr/lib/selenium /var/log/selenium /var/log/Xvfb

“screener” is a regular user we use for running Selenium and X server, you can use any user you want, but please do not use root.
Now we can download selenium standalone server:

cd /usr/lib/selenium ; wget http://selenium-release.storage.googleapis.com/2.40/selenium-server-standalone-2.40.0.jar

Make sure you downlowd the latest version.
Besides we definitely need Java:

yum install jre

To manage Selenium and Xvfb we use two simple scripts. Put them to /etc/init.d/selenium and /etc/init.d/Xvfb respectively.

#!/bin/bash
# selenium - this script starts and stops the selenium grid
#
# chkconfig:   - 85 15
# description: Selenium Grid is a distributed testing platform for browser-based automation.
# processname: selenium
# pidfile:     /etc/selenium/tmp/selenium.pid

# Source function library.
. /etc/rc.d/init.d/functions
selenium_dir=/usr/lib/selenium
log_dir=/var/log/selenium/
error_log=$log_dir/selenium_error.log
std_log=$log_dir/selenium_std.log
pid_file=/var/log/selenium/selenium.pid
java=/usr/bin/java
selenium="$selenium_dir/selenium-server-standalone-2.40.0.jar"
user=screener

start() {
    if test -f $pid_file
    then
        PID=`cat $pid_file`
        if  ps --pid $PID >/dev/null;
            then
                echo "Selenium is already running: $PID"
                exit 0
            else
                echo "Removing stale pid file: $pid_file"
        fi
    fi

    echo -n "Starting Selenium..."
    su $user -c "$java -jar $selenium -host 127.0.0.1 >$std_log 2>$error_log &"

    if [ $? == "0" ]; then
        success
    else
        failure
    fi
    echo
    ps  -C java -o pid,cmd | grep $selenium  | awk {'print $1 '} > $pid_file
}

stop() {
    if test -f $pid_file
    then
        echo -n "Stopping Selenium..."
        PID=`cat $pid_file`
        su $user -c "kill -3 $PID"
        if kill -9 $PID ;
                then
                        sleep 2
                        test -f $pid_file && rm -f $pid_file
                        success
                else
                        echo "Selenium could not be stopped..."
                        failure
                fi
    else
            echo "Selenium is not running."
            failure
    fi
    echo
}

status() {
    if test -f $pid_file
    then
        PID=`cat $pid_file`
        if  ps --pid $PID >/dev/null ;
        then
            echo "Selenium is running...$PID"
        else
            echo "Selenium isn't running..."
        fi
    else
            echo "Selenium isn't running..."
    fi
}

case "$1" in
    start)
        $1
        ;;
    stop)
        $1
        ;;
    restart)
        stop
        start
        ;;
    status)
        $1
        ;;
    *)
        echo "Usage: $SELF start|stop|restart|status"
        exit 1
    ;;
esac

Xvfb init.d script:

#!/bin/bash
# selenium - this script starts and stops Xvfb
#
# chkconfig:   - 85 15
# description: Xvfb
# processname: Xvfb
# pidfile:    /var/log/Xvfb/Xvfb.pid

# Source function library.
. /etc/rc.d/init.d/functions

#selenium_dir=/usr/lib/selenium
log_dir=/var/log/Xvfb
error_log=$log_dir/Xvfb_error.log
std_log=$log_dir/Xvfb_std.log
pid_file=/var/log/selenium/Xvfb.pid
java=/usr/bin/java
xvfb=$( which Xvfb )
user=screener

start() {
    if test -f $pid_file
    then
        PID=`cat $pid_file`
        if  ps --pid $PID >/dev/null;
            then
                echo "Xvfb is already running: $PID"
                exit 0
            else
                echo "Removing stale pid file: $pid_file"
        fi
    fi

    echo -n "Starting Xvfb..."
    su $user -c "$xvfb :99 -ac -screen 0 1280x1024x24 -nolisten tcp >$std_log 2>$error_log &"

    if [ $? == "0" ]; then
        success
    else
        failure
    fi
    echo
    ps  -C Xvfb -o pid,cmd | grep Xvfb  | awk {'print $1 '} > $pid_file
}

stop() {
    if test -f $pid_file
    then
        echo -n "Stopping Xvfb..."
        PID=`cat $pid_file`
        su $user -c "kill -15 $PID"
        if kill -9 $PID ;
                then
                        sleep 2
                        test -f $pid_file && rm -f $pid_file
                        success
                else
                        echo "Xvfb could not be stopped..."
                        failure
                fi
    else
            echo "Xvfb is not running."
            failure
    fi
    echo
}

status() {
    if test -f $pid_file
    then
        PID=`cat $pid_file`
        if  ps --pid $PID >/dev/null ;
        then
            echo "Xvfb is running...$PID"
        else
            echo "Xvfb isn't running..."
        fi
    else
            echo "Xvfb isn't running..."
    fi
}

case "$1" in
    start)
        $1
        ;;
    stop)
        $1
        ;;
    restart)
        stop
        start
        ;;
    status)
        $1
        ;;
    *)
        echo "Usage: $SELF start|stop|restart|status"
        exit 1
    ;;
esac

Now we are ready to start them both:

/etc/init.d/Xvfb start
/etc/init.d/selenium start

Let’s check if it works using selenium wrapper for Python:

python-pip install -U selenium

!/usr/bin/env python
   from selenium import webdriver
   browser = webdriver.Firefox()
   browser.get('http://supportex.net')
   browser.save_screenshot('supportex.png')
   browser.quit()

Now you should get supportex.png in your current directory.

Please also note that by default Selenium opens TCP port 4444 on all network interfaces. So for security reasons it’s strongly reccomended to limit an access to these port by firewall. For instance this way,

iptables -N SELENIUM
iptables -A INPUT -p tcp --dport 4444 -j SELENIUM
iptables -A SELENIUM -p tcp --dport 4444 -s 127.0.0.1 -j ACCEPT
iptables -A SELENIUM -p tcp --dport 4444 -j DROP