Saturday, May 24, 2014

functional selenium

Java 8 incorporated part of Google Guava library and provides nice support for functional programming, I found it extremely helpful when working with Selenium.

These is a new class in Java 8 called Supplier, it only has one method get() which will return the type you specified.

Then I used enum or organize the same By type into enum which implements Supplier,

0. By xpath

1. By cssSelector



2. By tagName



I should complete this TagName class with all HTML tags.

3. By name



4. By Others



and a Locator to locate one or a list of elements,



and a helper method to use ElementsLocator to find a Stream of Element meeting the criteria. ElementsLocator extends AbstractLocator which implements Function interface from Java 8.

Function is too generic so I extend it into another interface called Locator which is more suitable concept for Selenium tests, locating something from somewhere,



and use it in another page object to locate the Button,



This code snippet is to combine 3 functions into one,



Use the Supplier enum constants to locate elements on the form or page and either enter text or click it,

With static imports,



or with full class qualifier,





put(QUANTITY, 2) is to type the text "2" into an input field QUANTITY which is an enum constant in Name class to specify

By.name("cartDS.shoppingcart_ROW0_m_orderItemVector_ROW0_m_quantity") 


or another enum constant in Xpath class,
By.xpath("//div[@id='ys_cartInfo']/descendant::input[@name='cartDS.shoppingcart_ROW0_m_orderItemVector_ROW0_m_quantity']");


It doesn't care which selector you are using, as long as it implements Supplier interface so the framework can get a By selector from the parameter, if it is from Name, it is a ByName selector, if it is from Xpath, it is a ByXpath selector.

and button(UPDATE) is to locate the UPDATE button, UPDATE is an enum constant in CssSelector to specify

By.cssSelector("input[value='Update']");


The advantage of having this Supplier enums is to reduce the number of methods of the helper API, before I came out this Supplier solution, I have used the following approach,
findElementById,
findElementByName,
...
findElementByXpath,
findElementsById,
findElementsByName,
...
findElementsByXpath

While now I only have three methods,



Element findElement(By by);

This is same as the original Selenium SearchContext.findElement

Element untilFindElement(By by);

This is a new method after calling the original Selenium SearchContext.findElement it waits until an element is found or timeout

Stream findElements(By by); This is similar to original SearchContext.findElements, just it uses Stream rather than list, Stream is a new interface from Java 8. One implementation is in Browser interface as a default method, Element::new is called method refernece in Java 8, the following two code snippets have the same functionality while the latter one is using Lambda Expression

    default public Stream<Element> findElements(Supplier<By> by) {
        return findElements(by.get()).stream().map(Element::new);
    }

    default public Stream<Element> findElements(Supplier<By> by) {
        return findElements(by.get()).stream().map(element -> new Element(element));
    }



By using Supplier, you can hide Selenium By inside an enum and make the test codes very clean,

No comments:

Post a Comment