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 :
- attrEquals(String attrName, String expectedValue)
- hasClass(String className)
- textEquals(final String expected)
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