View on GitHub

selenium-utils

Takes the misery out of Selenium

Download this project as a .zip file Download this project as a tar.gz file

selenium-utils is a set of APIs that helps writing Selenium tests in a concise, fluent and effective way. It encapsulates the findElement, WebDriverWait and ExpectedConditions plumbing into a high-level, DSL-like API.
It also provides tooling for booting up Selenium, recording your tests as videos, and getting insights about failures.

Findr

Findr is a simple yet very powerful utility class that helps to write tests in a "wait-style", without accessing WebDriverWait directly.

The API is slick, easy to use and helps to be DRY and concise. It's based on chained methods in order to expose a clear API, and uses function composition in order to create chains of conditions. This chain is then evaluated atomically inside a WebDriverWait, under the hood.

Evaluation fails if the chain doesn't completely completes within a given timeout, and an exception is thrown.

Simple example over Google search :

// get google
driver.get("http://www.google.com");

// perform the search
new Findr(driver)           // create a Findr
    .elem(By.id("gbqfq"))  // wait for the elem located by id "gbqfq"
    .sendKeys("pojos on the web", Keys.ENTER);  // type the query

// check the results
new Findr(driver)           // create Findr
    .elem(By.id("ires"))    // wait for elem by id
    .elemList(By.cssSelector("h3.r")) // wait for a list of elements
    .at(0)                  // wait for 1st in the list
    .elem(By.tagName("a"))  // wait for some <a> tag under the first list elem
    .where(Findrs.textEquals("POJOs on the Web!: Woko")) // wait for the text in the link
    .eval();    // evaluate the whole stuff ! will block until success, or timeout

Built-in predicates

The Findrs class exposes a set of static factory methods that create Predicate<WebElement>s for the recurrent stuff, for example :

Those can be used directly in your findrs :

new Findr(driver)
    .elem(By.cssSelector("div.my-class"))
    .where(Findrs.attrEquals("my-attr", "my-value"))
    .where(Findrs.textEquals("This is some content"))
    .eval();

Error reporting

Findr tries to report failures in condition chains by including a String-ified version of the path. Of course, the stack trace of the Timeout exception will tell where the evaluation failed.

There are also variants to eval() that accept a failureMessage argument.

Understanding failures

Findr executes the various functions you compose as a "back box", and it's sometimes hard to understand where it went wrong in the conditions chain. In order to get insights about what's going on, you can set the sys prop webtests.findr.verbose, so that it outputs the logs (to stdout) when asserting the condition chain.

WebDriver init

Use DrivrBuilder in order to create instances of WebDriver. The API can be used statically :

// create a simple Chrome Driver
WebDriver driver = DriverBuildr
    .chrome()
    .setDriverPath(new File("/path/to/chromedriver"))
    .build();

Or by defining system properties :

WebDriver = DriverBuildr.fromSysProps().build();

The latter approach allows for more flexible builds.

System Properties

Here is a list of all supported System Properties :

property allowed values default comment
General props
webtests.browser firefox,chrome firefox
webtests.locales en, fr, ... Comma-separated list of locale(s) for the tests (browser language)
webtests.findr.timeout Any (reasonable) positive integer 10 The default Findr timeout in seconds
webtests.findr.verbose true,fase false log some infos about findr evaluation chains (helps debugging)
webtests.video.enabled true,false false enables video recording of failed tests
webtests.video.dir path to folder tmp dir
webtests.video.failures.only true,false true keep videos for failures only, or for all tests
Chrome only
webdriver.chrome.driver path to driver exe mandatory for Chrome

TestCase plumbing

Base classes are included that manage the driver init/close and video stuff. If you use JUnit for example, you simply have to extend a base class :

public class MyTest extends ManagedDriverJunit4TestBase {

    @Test
    public void testMe()  {
        WebDriver d = getWebDriver();
        ...
    }

}

Doing so will allow you to run your test directly, and parameterize it using sys props.

There's also a TestUtil class that implements the lifecycle of a typical test. You can delegate to that one if you already extend a base class in your test.

Video recording

We have a very basic ScreenRecordr class that performs video capture on the host that runs the webdriver. It's activated by the TestCase plumbing, via sys props.

It's built on Monte Media Library, and is pure Java. It's been tested on a different platforms (mac, windows, linux), and even seems to work in headless/xvfb environments.

Using with Maven

Add the dependency to your pom :

<dependency>
    <groupId>com.pojosontheweb</groupId>
    <artifactId>selenium-utils-core</artifactId>
    <version>LATEST-SNAPSHOT</version>
    <scope>test</scope>
</dependency>

Configure surefire :

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <systemPropertyVariables>
            <webtests.browser>${webtests.browser}</webtests.browser>
            <webtests.video.enabled>${webtests.video.enabled}</webtests.video.enabled>
            <webtests.video.dir>${project.build.directory}/webtests-videos</webtests.video.dir>
            <webdriver.chrome.driver>${webdriver.chrome.driver}</webdriver.chrome.driver>
        </systemPropertyVariables>
    </configuration>
</plugin>

Invoke maven :

$> mvn test

With sys props :

$> mvn test -Dwebtests.browser=chrome -Dwebdriver.chrome.driver=/opt/chromedriver -Dwebtests.video.enabled=true