Running your Selenium tests in parallel: Perl

The Test::WWW::Selenium does a good integration of Selenium with Perl testing framework. However by default, tests runs in sequence. One easy solution is to use the Thread::Pool::Simple module to run the test functions in parallel. Short testing showed that this parallel version ran for 19 seconds vs 39 seconds for the serial run.

#!/usr/bin/env perl

use Thread::Pool::Simple;
use Test::More "no_plan";
use Test::WWW::Selenium;

sub test_page() {
    my ($url, $input, $button) = @_;

    my $sel = Test::WWW::Selenium->new(
        host => "localhost",
        port => 4444,
        browser => "*firefox",
        browser_url => $url,
    );

    $sel->open($url);
    $sel->type_ok($input, "Sauce Labs");
    $sel->click_ok($button);
    $sel->wait_for_page_to_load_ok(5000);
    $sel->body_text_like(qr/Selenium/);
    $sel->close();
}

sub test_google() {
    &test_page("http://google.com", "q", "btnG");
}

sub test_yahoo() {
    &test_page("http://yahoo.com", "p", "search-submit");
}

sub test_bing() {
    &test_page("http://www.bing.com", "q", "go");
}

sub worker() {
    my $func = shift;
    # We can't pass CODE items to a pool, so we use eval
    eval("&$func();");
}

my @names = (
    "test_google",
    "test_yahoo",
    "test_bing",
);

my $pool = Thread::Pool::Simple->new(
    min => 3,
    max => 10,
    do => [\&worker],
);

foreach $name (@names) {
    print "Adding $name\n";
    $pool->add($name);
}
$pool->join();

Converting the code to work with Sauce OnDemand is a breeze as well, we will also use the job-name attribute to assign name for our jobs.

#!/usr/bin/env perl

use Thread::Pool::Simple;
use Test::More "no_plan";
use Test::WWW::Selenium;

# Replace username and access-key with your real
# Sauce OnDemand username and access-key
my $settings = qq(
{
    "username" : "YOUR-SAUCE-USER-NAME",
    "access-key" : "YOUR-SAUCE-ACCESS-KEY",
    "os" : "Linux",
    "browser" : "firefox",
    "browser-version" : ""
    "job-name" : "_JOB_NAME_"
}
);

sub test_page() {
    my ($name, $url, $input, $button) = @_;

    my $opts = $settings;
    $opts =~ s/_JOB_NAME_/$name/; # Add job name

    my $sel = Test::WWW::Selenium->new(
        host => "ondemand.saucelabs.com",
        port => 80,
        browser => $opts,
        browser_url => $url,
    );

    $sel->open($url);
    $sel->type_ok($input, "Sauce Labs");
    $sel->click_ok($button);
    $sel->wait_for_page_to_load_ok(5000);
    $sel->body_text_like(qr/Selenium/);
    $sel->close();
}

sub test_google() {
    &test_page("Google", "http://google.com", "q", "btnG");
}

sub test_yahoo() {
    &test_page("Yahoo", "http://yahoo.com", "p", "search-submit");
}

sub test_bing() {
    &test_page("Bing", "http://www.bing.com", "q", "go");
}

sub worker() {
    my $func = shift;
    # We can't pass CODE items to a pool, so we use eval
    eval("&$func();");
}

my @names = (
    "test_google",
    "test_yahoo",
    "test_bing",
);

my $pool = Thread::Pool::Simple->new(
    min => 3,
    max => 10,
    do => [\&worker],
);

foreach $name (@names) {
    print "Adding $name\n";
    $pool->add($name);
}
$pool->join();

Written by

The Sauce Labs Team

Topics

SeleniumIntegrations