Posts Tagged ‘testing’

Selenium: XPath marks the spot

January 6th, 2011 by joe

Selenium doesn’t speak English

A webpage looks different to a computer than to a human (and if you are computer, you can skip this blog post). Most humans don’t care how computers look at web pages, but it may matter to you. That’s because if you are writing a Selenium test, you have to know how to see a web page the way a computer does.

A webpage has things on it. The text you’re reading now isn’t a blog post to a computer – it’s a thing in a webpage. So are all the links and buttons on this page. If you were writing a Selenium test that wanted to come to this page and check for text, that would be easy; you just need to know the URL for the page and the text to look for. If you wanted to write a test that came to this page and clicked on a button, that would be more difficult. You’d need to know how to tell the computer which button to click on. That’s what XPath is for. It stands for “XML path”; XML is (loosely) the language your page is written in, and path means the path to an element (in this case a button) in the page.

XPath leads to the spot

Everything in the page is an element. Most of them live inside another element. The code for an element looks like this: <type>content</type>. “Type” is what kind of element it is; input, paragraph, division (of the page), etc. Where you see <type> is where the element’s code begins, and </type> is where it ends. The content in the middle is some combination of text that is going to appear in the webpage and lists of other elements. Those elements can contain other elements. When you’re looking at a page, those elements are visible as rectangles, with backgrounds or images or text in them. They live inside bigger rectangles that partition up the page. Those rectangles probably live inside even bigger rectangles.

The “path” to an element is the list of elements it lives inside. To construct one, you list all the elements it lives inside, starting with the top one. You can also do fancier things, like scan the whole page for certain kinds of elements, or some specific kind of element but only when it’s inside another certain kind of element. It wouldn’t make sense for me to explain how to write an XPath here, because w3schools has totally got that covered.

Don’t use XPath

There are two important things to say about XPath with regards to Selenium. First, XPaths are only one of three ways to tell Selenium how to locate an element, and it’s the worst one. Second, that if you must use XPath locators, there are some steps you should take to keep them from breaking or slowing the script down when they’re being used.

They’re the worst kind of selector to use because of their brittleness and their slowness. If you can, you should use unique id attributes on the elements you’re trying to interact with through Selenium. If that fails, you should try to use CSS selectors.

If you have to use XPath, be careful

If you must use XPaths, you have to make a tradeoff between reliability and speed.
A fast xpath might say “click on the third input element in the second div in the fourth paragraph in the body element of the html element.” It’s like having a treasure map that says “Turn left at the first fork in the path, then right, then your third left.” That’s easy to follow, but if someone adds another fork to the trail anywhere along that path, those directions will lead somewhere else. An XPath like that will be interpreted quickly by Selenium, but it will stop working as soon as you put another div along the path through the page.

A more reliable XPath might say “I want you to find an element that is an input element inside any paragraph inside any element that is inside any div as long as it has a ‘name’ attribute of ‘blah’ .” It’s like a treasure map that says “You want the spot between two pine trees that are 100 feet to the west of an oak tree that’s just after a left turn in the trail.” Those directions will stand the test of time, but when you need to follow them, you’ll have to check every left turn, every oak tree, and every pine tree on the island to be sure. This is less brittle, but it will force the browser to check a lot more things on the page before it can be sure it has found what you want.

Share

Make Sauce TV Even More Fun To Watch (And Share!)

November 30th, 2010 by Santiago Suarez Ordoñez

After listening to @hugs talk about automating screencasts, and his belief that iphone-like commercials are the future of testing and marketing, I decided to take action and find a way to integrate the missing piece that would allow users to create audience-oriented videos using Selenium.

Surprisingly, it took only a few minutes of hacking to come up with a usable javascript extension that transforms the standard setContext Selenium command into a more descriptive and visual message, making videos recorded by Sauce OnDemand way more useful for external watchers.

What this code does is rewrite the original setContext browser side javascript to create and inject a semi-transparent div on top of the application window. All you have to do is tell your story using setContext while your test is running. After a few seconds, a fadeout function is called to take care of removing that div in a visually attractive way.

Here’s an example video of this feature. You can also check the job results page in Sauce and the source code of the test used to record it.

If you’d like to use this cool feature in your own OnDemand tests, just add the following JSON key to your browser string and start using setContext in them:

"user-extensions-url": "http://saucelabs.com/ext/setContext.js",

You can also change the styling of the message pretty easily. Just fork the gist, start playing around, and please share your updates with the rest in the comments section!

Share

#SFSE Meetup: Cloud Testing At Salesforce & Selenium 2 Update

November 29th, 2010 by Ashley Wilson

Here are videos from another great San Francisco Selenium Meetup. Chris Chen, of Salesforce, gave attendees a special look in to how Salesforce automates testing in the cloud, and Eran Messeri, a core Selenium committer at Google, shared a new API for Selenium 2.

Check ‘em out!


How Salesforce Tests in the Cloud with Chris Chen


Selenium 2 API & Interactions with Eran Messeri

Share

The Future of Testing

November 18th, 2010 by The Sauce Labs Team

It’s gratifying to have one’s story mirrored by people like Elisabeth Hendrickson and Chris McMahon, but you’ve got to wonder if we’re all not just drinking the same koolaid. We think there’s more to this than sugar, water and artificial coloring. We think there’s a fundamental change in software development going on.

We welcome your comments after you read this post or add your voice to the habanero chorus by taking our new Selenium Survey. We’ll give you a bottle of Sauce Labs Hot Sauce for your time.

The question we pose to you: What’s the future of testing?

If you are a QA director or programmer who writes tests and follows Sauce Labs, you probably know agile development is exploding. You also most likely agree that Selenium is the lead technology in test automation programming and that automated testing is transforming software development. These ideas should, in our view, be obvious to development teams who are running hundreds or thousands of Selenium scripts in their deployment process, and perhaps with our browsers in the cloud.

This shift doesn’t seem to be an opinion or a hunch. Here’s a bit of evidence that the movement is gaining momentum.

On October 20th, Elisabeth Hendrickson from Quality Tree Software wrote a blog post questioning whether the rise in agile is affecting the QA job market in terms of skills and qualifications. She found it did – and that a trend is emerging.

On October 29th, Chris McMahon wrote another blog post, referencing Elisabeth Hendrickson and our very own Jason Huggins, in which Chris posited that we were seeing a “general across-the-board increase in demand for technical skills in traditional UI-based software testing”.

We couldn’t agree more. Agile development and continuous integration is changing the job landscape for testing professionals, and the technical skills being increasingly demanded is Selenium because the traditional definition of a QA skill-set just doesn’t work if teams want to be agile.

What of “Traditional” QA?

“I have seen a number of reports of a radical increase in the rate of adoption of agile practices among US companies of every size and description,” wrote McMahon. “And the agile whole-team approach to software development makes dedicated, siloed traditional Q&A test departments irrelevant.”

Wow. Do you think that QA departments are irrelevant?

Well, instead of traditional QA departments, we are finding that Quality Directors now know Selenium or Watir, among other technologies. They make sure that their development team uses modern testing practices, and developers write user interface acceptance tests with Selenium for features they have developed. As teams roll out features and fixes, they re-run tests against their applications at blazing speeds, get feedback, make fixes, re-run tests – and soon the new feature is deployed, failure free across all browsers. In other words, the trend we have seen amongst our customers concurs with McMahon.

Automated cross browser testing with Selenium: highly sought?

Growing evidence indicates the answer is yes, Selenium experience is a highly sought after skill.

Elizabeth Hendrickson set out to quantify the demand for programming skills for a small sample of tester job postings. They found that out of the 187 jobs sampled, 112 indicated that programming was required and 39 jobs indicated that programming was desired. In other words, a whopping 80% of test jobs in her sample required or desired programming skills.

Hendrickson’s research then considered test automation technologies. Out of their sample, 27 job ads explicitly said that they require knowledge of test automation tools and an additional 50 ads stated that test automation tool knowledge is a nice to have. We imagine many more actually required automation.

Hendrickson’s study makes a case for Selenium being the leader with the counts of jobs specifying an automation technology:

  • Selenium, including Selenium RC (31)
  • QTP (19)
  • XUnit frameworks such as JUnit, NUnit, TestNG, etc. (14)
  • LoadRunner (11)
  • JMeter (7)
  • Winrunner (7)
  • SilkTest (6)
  • SilkPerformer (4)
  • Visual Studio/TFS (4)
  • Watir or Watin (4)
  • Eggplant (2)
  • Fitnesse (2)

The present (and future) of testing is automated.

We’ve blogged about Selenium job trends already, so we were not surprised by Hendrickson’s report of Selenium skills seemed the most commonly required test automation technology.

Don’t believe us? Just check the QTP vs Selenium Job posting trends from Indeed.com below. Based on these trends, we’re going out on a pretty solid limb and predicting that openings for Selenium jobs will overtake QTP jobs within the next two years.

So, if you’re QA tester serious about a career in Quality and you don’t know a programming language like Java, Ruby, Python, and now JavaScript, learn one. However, you should be adept in automation technologies. We suggest, for obvious reasons, you learn Selenium.

And if you are a QA tester who already knows Selenium who hasn’t tried sending your tests to our browsers in the cloud? We’re always happy to hear yourfeedback on our service after you try it.

We welcome your comments!

Share

Introducing Bamboo Sauce

November 8th, 2010 by Ashley Wilson

The Sauce Labs ecosystem continues to happily grow with the addition of a new CI plugin built specifically for Sauce OnDemand.

Bamboo Sauce, created by Systems Bliss engineer Jonathan Doklovic, enables Bamboo users to easily integrate with Sauce OnDemand to run builds across multiple browsers in parallel. Sign up for the live demo this Thursday at 11 am PST to see how this pairing will speed up your test cycles and reduce the need for costly test infrastructure, while leveraging your existing investment in Bamboo, the popular Atlassian CI server.

Setup is simple, needing only a one-time admin configuration, and builds can be customized to test on a variety of OS/Browser combinations. Bamboo Sauce takes care of Firewall problems by creating a direct SSH tunnel from the cloud to a specific server (when you use Sauce OnDemand, we never see your code – rather, it stays behind whatever guard you have in place), and it also lets you pick which tests you want recorded via Sauce TV.

To find out more, tune in to the live demo, led by Systems Bliss and Sauce Labs. Jonathan of Sysbliss will walk you through setting up Bamboo Sauce, and also answer any questions. To follow along step-by-step, download the plugin and sign up for a free 30 day trial of Sauce OnDemand.

(To note – Bamboo Sauce is still in beta. We’d love to hear any feedback you have, so please let us know!)

Share

Doing Continuous Integration Testing? Check Out Our CI API

November 4th, 2010 by Santiago Suarez Ordoñez

Testing is a key ingredient in CI. And so in the constant effort to make our service insanely flexible and easy to use, we’ve released a brand new API that will help Continuous Integration servers such as Hudson, Bamboo, and TeamCity send Sauce OnDemand some interesting information about each test they run.

This includes the pass/fail final status of the test, the build number in which it was run, and even your own custom data.

Since some of this new job info is known only when the test finishes (e.g. whether the job passed or not), we’ve created not only one but two alternative methods for the info to be sent – the REST API and Selenium’s standard setContext command. Both are documented in our docs, and anyone interested can use them right now.

Once the info makes it to our end, it will be appropriately displayed with each job, helping our users find and group their jobs with less effort. (Notice that some of the work needed to display this in a cool way is still in our TODO – but the data is there). You can see some example jobs [1] with this data set or just create your own!

In the near future, we’ll be working with the authors of the Hudson and Bamboo plugins to add support for these CI APIs.

If you’re interested in hearing more, trying early versions of these plugins, or would like to see a plugin for another CI system, feel free to ping us here!

[1] https://saucelabs.com/jobs/4baf3ad0cd8b686ef554135c4addf621,
https://saucelabs.com/jobs/0d925d14c23888b8561215360b70a78d

Share

Sauce Labs Solves Real-World Selenium Issues

October 27th, 2010 by The Sauce Labs Team

Here at Sauce Labs, we work with, quite literally, millions of real-world Selenium tests. A handful of major issues with Selenium come up again and again with our customers. Some are browser configuration details we can tweak in our cloud, while others are beyond the reach of Selenium 1.

The good news for the Selenium community is that Selenium 2 aims to solve these latter issues in a comprehensive way. Most current Selenium users, however, have yet to make the leap to Selenium 2. In the meantime, we’ve been working around some of the biggest issues to make things work well for users of Sauce OnDemand.

First, we’ve brought Sauce OnDemand up to the best Selenium can be for SSL support and Unicode handling. On top of that, we’ve implemented a cross-browser file upload command that works in all versions of Internet Explorer, Safari, and Firefox, along with an automatic OS-level popup killer that deals with Internet Explorer’s tendency to break JavaScript testing environments with modal dialogs.

SSL Support

Testing with SSL is challenging. Testing with Selenium and SSL can seem downright impossible. But it doesn’t have to be so! Selenium users encounter two major issues with SSL: Selenium’s proxy mode interfering with the SSL connection and invalid certificates in staging environments. The solution to both is to use the Selenium project’s “CyberVillains” Certificate Authority.

In browsers other than Internet Explorer and Firefox, Selenium proxies all HTTP and HTTPS traffic. Browsers correctly recognize that the HTTPS connection is being interfered with and prevent Selenium from doing its job. The solution that the Selenium project came up with is to have Selenium’s proxy server sign HTTPS connections itself, using the CyberVillains CA. Once you install this certificate to the Trusted Root Certificate Authorities on Windows, Selenium can re-sign all HTTPS connections with a certificate signed by CyberVillains, allowing Selenium to drive your browser to HTTPS URLs. By default, it will do its best to validate the certificate used to sign the connection Selenium makes to your AUT, but that, too, can be dealt with.

Many teams use SSL certificates in testing that aren’t entirely valid. This presents a problem for in-browser testing, because browsers will complain about the SSL certificates being invalid on HTTPS URLs. CyberVillains to the rescue again! When browsers are configured to use Selenium to proxy all content, Selenium can be launched with -trustAllSSLCertificates, which turns off Selenium’s SSL certificate validation and accepts anything. In *iexplore and *firefox modes (AKA *iehta and *chrome, respectively), browsers are not configured to use Selenium as a proxy server, which usually means the CyberVillains approach doesn’t work. In Sauce OnDemand, we now automatically configure Internet Explorer to use Selenium as a proxy server, even in *iexplore mode. For Firefox, we’ve implemented custom profiles, which give you even more control over your certificate environment.

You can read more about our HTTPS support in the Sauce OnDemand documentation.

Unicode

We have now enabled full Unicode support for all browsers in Sauce OnDemand. We’ve installed East Asian Language support on all of our Windows robots, so your Unicode will render correctly in screenshots/videos, and you can now make assertions in your tests using unicode characters.

Cross-browser File Upload

File upload has been a persistent problem for Selenium users. We are excited to announce our preliminary support for cross-browser file upload in Sauce OnDemand. We’ve stepped out of the browser environment to interact directly with the operating system (only Windows for now), downloading files you specify from a URL and entering them into file input fields. We have this working on all versions of Internet Explorer, Safari, and Firefox on Windows, and we look forward to open-sourcing our work when we’ve flushed out the last few issues with it.

We’ve implemented file upload support using the existing attachFile command in Selenium, so you don’t even need to change your tests that already work in Firefox. Just call attachFile with a URL to the file you want uploaded and a Selenium locator for the file input box, and we’ll take care of the rest!

OS-level Popup Eliminator

Using the same OS-level hooks we built for cross-browser file upload, we’ve built a popup eliminator that will automatically click “OK” or “Yes” in any browser-level popups that appear in Internet Explorer. For example, even with the CyberVillains certificate, IE 6, 7, and 8 will pop up warnings when opening a page with mixed HTTP/HTTPS content. Our popup eliminator takes care of these and more popups. As we gain confidence in this approach, we look forward to pushing these changes upstream into the Selenium project or open-sourcing them as a standalone wrapper for Selenium.

At Sauce Labs, we’re all about providing the best Selenium experience possible. If you have suggestions for how we could improve these features, let us know on our forums!

Share

JUnit 4 and Selenium – Part Three: Parallelism and OnDemand

October 25th, 2010 by Adam Goucher

This is the third and final part of how to create Selenium scripts to run in parallel. Today we get into the meat of things with parallel execution, both locally and in OnDemand.

JUnit 4 doesn’t ship with a true parallel solution, but Harald Wellman wrote a blog post on running parameterized JUnit tests in parallel, which does just about everything we want. But instead of having a fixed thread pool side, I modified it to use a dynamically sized one. With this new class in your project, we only need to change the @RunWith line to use our new parallel, parameterized runner.

@RunWith(Parallelized.class)

Now the execution profile is as follows: For each item in the @Parameters Collection, launch a new thread to execute the current test method in parallel. Have two browser strings? Then two threads run in parallel. Have eight browser strings? Well, that is eight threads running in parallel. This means that however long it takes run a single browser through all your scripts, that’s how long it will take to run against any number of browsers.

Win!

Well, sure, but only if you have all those browsers on your machine or have an Se-Grid installation at your disposal. Most people do not have either luxury, so that is where Sauce OnDemand comes into the mix. Hosted in the cloud, you don’t need to have the browsers on your machine or even behind the firewall. With some minor modification to our example class, we can have a script that runs locally or in the OnDemand cloud.

package com.saucelabs.cc;

import java.util.Collection;
import java.util.List;
import java.util.LinkedList;
import java.util.Arrays;

import java.io.InputStream;
import java.util.Properties;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.FieldNamingPolicy;

import com.saucelabs.junit.Parallelized;
import com.saucelabs.ondemand.ConnectionParameters;

@RunWith(Parallelized.class)
public class TestParallelOnDemand {
    private Selenium selenium;
    private String browser;
    private String browserVersion;
    private String os;
    public static Properties browserProps = new Properties();
    private Properties parallelProps = new Properties();
    private String json;

    public TestParallelOnDemand(String os, String browser, String version) throws Exception {
        super();
        this.browser = browser;
        this.browserVersion = version;
        this.os = os;

        InputStream is = this.getClass().getResourceAsStream("/parallel.properties");
        parallelProps.load(is);

        if (parallelProps.getProperty("ondemand").equals("true")) {
          Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create();
          ConnectionParameters od = new ConnectionParameters();
          od.setBrowser(this.browser);
          od.browserVersion = this.browserVersion;
          od.jobName = this.getClass().getName();
          od.os = this.os;
          this.json = gson.toJson(od);
        }
    }

    @Parameters
    public static LinkedList browsersStrings() throws Exception {
      LinkedList browsers = new LinkedList();

      InputStream is = TestParallelOnDemand.class.getResourceAsStream("/browser.properties");
      browserProps.load(is);

      String[] rawBrowserStrings = browserProps.getProperty("browsers").split(",");
      for (String rawBrowserString : rawBrowserStrings) {
        if (rawBrowserString.indexOf(";") != -1) {
          String[] browserParts = rawBrowserString.split(";");
          browsers.add(new String[] { browserParts[0], browserParts[1], browserParts[2] });
        } else {
          browsers.add(new String[] { rawBrowserString, "", "" });
        }
      }
      return browsers;
    }

    @Before
    public void setUp() throws Exception  {
        if (parallelProps.getProperty("ondemand").equals("true")) {
          selenium = new DefaultSelenium("saucelabs.com", 4444, this.json, "http://saucelabs.com");
        } else {
          selenium = new DefaultSelenium("localhost", 4444, this.browser, "http://localhost:3000");
        }
        selenium.start();
        selenium.setTimeout("90000");
        selenium.windowMaximize();
    }

    @Test
    public void testSauce() throws Exception {
        this.selenium.open("/");
        assertEquals("Cross browser testing with Selenium - Sauce Labs", this.selenium.getTitle());
    }

    @After
    public void tearDown() throws Exception {
        selenium.stop();
    }
}

So what has changed?

The @Parameters are loaded differently again. When the script was only ever going to run locally, the browser string provided enough information. But since it’s also going to be run in OnDemand, there needs to be some extra information for the OS and browser version. Using the same pattern as part two, the browser string information has been moved from the test code and into an external properties file.

# order is important! - OS/browser/version
browsers=Windows 2003;*firefox;3.6.,Windows 2003;*googlechrome;3.,

OnDemand integrates with Selenium scripts by sending a JSON string with the configuration information to their server, which is why there is a new decision in the @Before method. An important design pattern when creating scripts that run locally or in some other environment is to add a switch in the script to determine where to go. You don’t want to have to modify code itself in order to run between different environments.

The JSON itself is an interesting challenge. Just as changing the source code didn’t make sense when parameterizing the individual browser strings, hard coding the JSON doesn’t make much sense either. To build the JSON in code, the Gson library was used. Here is the ConnectionParameters class that Gson is building the connection information from.

package com.saucelabs.ondemand;

import java.io.InputStream;
import java.util.Properties;

public class ConnectionParameters {
  // hyphenated strings like access-key and browser-version need to be camel-case
  public String username;
  public String accessKey;
  public String os;
  private String browser;
  public String browserVersion;
  public String jobName;

  // gson does not convert transient fields
  private transient String propertiesFile = "/ondemand.properties";
  private transient Properties ondemandProperties = new Properties();

  public ConnectionParameters() throws Exception {
    InputStream is = this.getClass().getResourceAsStream(propertiesFile);
    ondemandProperties.load(is);
    this.username = ondemandProperties.getProperty("username");
    this.accessKey = ondemandProperties.getProperty("access-key");
  }

  // ondemand browser strings cant start with the *
  public void setBrowser(String browser) {
    if (browser.startsWith("*")) {
      this.browser = browser.substring(1);
    }
  }

  public String getBrowser() {
    return this.browser;
  }
}

It should also be no surprise by now that the username and access key is loaded from an external file, as that can change and we wouldn’t want to recompile for something like that.

The only other thing of interest is the getter/setter for the browser string. Recall that the string can be used by either the local Se-RC server or OnDemand. Currently, OnDemand does not like having the * at the beginning, so we take care of that behind the scenes from the script.

With this bit of infrastructure in place, we can run our scripts against any OS/Browser/Version combination that OnDemand currently supports or you have handy on your own hardware, and it will automatically scale execution to run each method in parallel.

The example used here is a little extreme as the test itself is really only two lines, but in a real implementation scenario I would:

  • Create a custom formatter for Selenium IDE (or Sauce IDE) so when you export the recorded script, it has everything your particular framework requires
  • Create a class hierarchy for the scripts to move all the non @Test methods out of the class to make it a bit tidier

And so concludes the series on how to run Selenium scripts in parallel using JUnit 4. To see the full project, including the Maven pom, check out Parallel Test Examples in the Github repo.

Share

Parallel JUnit 4 and Selenium – Part Two: External Properties

October 22nd, 2010 by Adam Goucher

In the first part of this series, we saw how to make use of the Parameterized runner that comes with JUnit 4 to execute our tests across multiple browsers. But that came with two penalties. The increasing test duration will be addressed in part three, but today we’ll address how to modify the browsers used without having to recompile our test.

As someone who sees a lot of different teams’ Selenium code, I’m appalled at how often a simple change, such as adjusting which browser(s) to run against, requires a recompile. To me, that is a nasty code smell. Things that do not materially affect the purpose of the script and are often changing should be externalized out of the code. In our case, we are going to use a Properties file inside the @Parameters method. Nothing else in the script has changed.

@Parameters
public static LinkedList browsersStrings() throws Exception {
  LinkedList browsers = new LinkedList();

  InputStream is = TestProperties.class.getResourceAsStream("/environment.properties");
  environmentProps.load(is);

  String[] rawBrowserStrings = environmentProps.getProperty("browsers").split(",");
  for (String rawBrowserString : rawBrowserStrings) {
    browsers.add(new String[] { rawBrowserString });
  }
  return browsers;
}

Nothing special here, just standard loading of a properties file that has a browsers key, which is a comma separated list of allowable Selenium browser strings. Now the same compiled code will run different browsers without actually change the Java code.

The result is much nicer, but still has to address the problem of a massive increase in execution. That will be the subject of part three of this series.

To see the full source code the snippet was pulled from, see TestProperties.java

Share

Parallel JUnit 4 and Selenium – Part One: Parameters

October 20th, 2010 by Adam Goucher

Though not the first testing framework created, JUnit has garnered plenty of mainstream attention and created a wave of followers.

One of these is TestNG, which was created to address, among other things, the difficulty of passing parameters into scripts, as well as run scripts in parallel. For these reasons, the Selenium community has largely chosen TestNG as the framework of choice. But JUnit 4 can also address these topics using the @RunWith annotation and a custom runner.

This is part one in a series of posts illustrating how to create parameterized, parallel scripts that can be run either on your local infrastructure or on Sauce OnDemand.

Before we start into code, here’s a bit of context. I’ve been writing test frameworks and harnesses for a decade now and know the frustration of finding half answers online that have been stripped down for easy blog post write-ups. The intent of my post is to not inflict you with that experience. Instead, I’ll discuss parallel execution in the context of a full project – complete with Maven pom.xml and all headers in their proper places. There will be places in this post where they are omitted, so you are recommended to clone the Sauce Labs Parallel Test Examples github repo.

We begin our journey with @RunWith(Parameterized.class). This annotation will iterate over each test method with a set of parameters of your choosing. For Selenium scripts, the part that begs for parallelization is the browser, so that is what we will parameterize first.

Have a look at TestParameters.java, as there are a couple of interesting things beyond the standard @Before, @Test and @After.

package com.saucelabs.cc;

import java.util.Collection;
import java.util.List;
import java.util.Arrays;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;

@RunWith(Parameterized.class)
public class TestParameters {
    private Selenium selenium;
    private String browser;

    public TestParameters(String browser){
        super();
        this.browser = browser;
    }

    @Parameters
    public static Collection browsersStrings(){
      return Arrays.asList(new Object[][] { {"*firefox"}, {"*googlechrome"} });
    }

    @Before
    public void setUp() throws Exception {
        selenium = new DefaultSelenium("localhost", 4444, this.browser, "http://localhost:3000");
        selenium.start();
        selenium.setTimeout("90000");
        selenium.windowMaximize();
    }

    @Test
    public void testSauce() throws Exception {
        this.selenium.open("/");
        assertEquals("Cross browser testing with Selenium - Sauce Labs", this.selenium.getTitle());
    }

    @After
    public void tearDown() throws Exception {
        selenium.stop();
    }
}

The first thing that sticks out is the @RunWith class annotation. While there are a number of different runners floating around, we are using the Parameterized one that comes with JUnit itself. With this in place, JUnit will look for a method annotated with @Parameters to use as arguments to the constructor. In this case, we currently only have one parameter, which we set as this.browser. Of course, this is then used when launching the browser in @Before.

And how does this address parallelization? Well, the @parameters method must return a Collection that is then iterated over, so you end up executing testSauce twice – the first time with “*firefox” and then with “*googlechrome.”

This is cool from a technical perspective, but has two pretty big flaws. First, the Java code needs to be edited to change browsers, and second, by executing serially, the time it takes to run the scripts increases 100% for every browser added. We’ll address both of those starting with external loading of our @Parameters in the next post of the series.

Share