Saturday, October 11, 2014

How Java 8 can simplify test automation

In this short article we'll take a look at some useful Java 8 tricks, that may help us to simplify automated tests development process.

As you know, there were added lots of interesting features: lambdas, functional interfaces, streams, etc. And I guess beginners or old school developers may find this info subtle or even difficult. But I believe when you dig deeper, you'll see how could it be easier to implement some features we spent lots of code lines on before.

One of the most popular problem we face with while web automation is timeouts. Selenium fans know how could it be painful to implement scenarios for modern JS-based web applications with lots of dynamic components. End-users can't even imaging what's the price of fancy layout in terms of automation effort.

There're number of techniques for resolving tests' failures related to elements' presence, visibility, etc. issues. One of them is using explicit waits aka WebDriverWait with ExpectedConditions.

Generally, we can create some custom findElement implementation to force webdriver wait for some element's visibility for <= N time units:


And it will work for common scenarios. But what if we want to wait for some other condition, like element to be clickable? In such case we would need to create some overloaded methods or for example prepare an enum to pass its values to findElement and call appropriate condition depending on inputs. In other words we would need to create some workaround to make our search method generic. With Java 8 this task can be easily done via functional interfaces:


As you see, we pass a function of 1 input - By, and 1 output - ExpectedCondition<WebElement>. And now let's look at ExpectedConditions class:


Both methods have the same input / output. It means that they match our function definition. Well, let's take a look how easy we can pass above methods' references to our findElement implementation:


As you can see new language level provides us a way of static methods referring. Technically, you can refer even objects' methods, but it's out of scope of this article.

Besides methods' references, you can also see Optional, that is a built-in functional interface. It's pretty good for objects' validation, that may potentially be null. It supports flexible filtering and setting default value, if input argument is null.

Let's move on and take a look at example of getting text from a list of nodes:


It's pretty straightforward: we just looping though WebElements list to retrieve text and put it into new array list. And now let's look at how could it be implemented via Java 8 streams:


We use stream for looping through the list of WebElements. Map converts each element into another object via the given function. In our case we call WebElement::getText, trim and save it into a new object. Then we use collect for putting all text nodes into a new list. Collectors helps to identify output collection type. It's a very useful utility class that you definitely should take a look at.

Our last example is a little bit more complicated. It's based on a previous article source code. If you remember, we used custom TestNG listener for retrieving basic test results info and populating Mustache template structure. We had to loop through nested complicated collections for getting different entities, and also sort intermediate and final objects' lists.


Let's see what Java 8 provides for the same task:


First of all, for loops were replaced with streamsif was replaced with filter. Then we used map to apply the following complicated transformation.

One the main goals was to get test context from internal TestNG collections and save it inside custom TestResult entity for further parsing. So we had to loop through each test result group - suite.getResults().values().stream() - and put appropriate item - results.getTestContext() - to implicit intermediate collection List<TestResult>. By the way, sorted helped us to apply custom comparator on fly.

To save test results' logical hierarchy, this collection was passed as a constructor parameter to Suite entity. Last steps were the same as in case of intermediate collection preparation: calling sorted with custom lambda comparator and putting Suite objects into output List<Suite>.

That's pretty much it. Now you're aware of some latest Java 8 features and its positive impact on automation efforts.

No comments:

Post a Comment