Java is one of the most popular and widely used programming languages in software development and test automation frameworks. With each major release, it introduces new features thus enabling automation developers to enhance the testing frameworks and processes. In this article, we will explore how we can use the New Java features for test automation framework to make it more elegant and readable.
1) Java 8 ForEach Loop
The forEach loop was introduced in Java 8 to iterate over a collection and perform an action on each element. Since Java 8 we can pass Lambda expressions. Earlier we used to iterate over a list by using for loop or enhanced for loop. Look at the below example code:
1) Navigate to the Website.
2) Fetch all matching Web elements in a list.
3) Display the text.
Old Approach
@Test
public void TestHeaderLinks() throws InterruptedException {
WebDriver driver= LocalDriverFactory.getDriver();
driver.get("https://www.amazon.com/");
List<WebElement>list=driver.findElements(By.xpath("//div[@id='nav-xshop']//a"));
for(WebElement ele : list)
{
System.out.print(ele.getText());
}
New Approach
In the above code snippet, we can replace the for loop with a forEach loop. This expression starts with the name of the collection(list) and inside forEach, we have taken a single variable (ele) which is final and local and can not be used outside of this method call. (->) signifies lambda and we have assigned the action(ele.getText()) to the variable. Simple and looks more readable.
list.forEach(ele->System.out.print(ele.getText()));
forEach to loop a map: Suppose We have a map collection and we need to display all key values of this collection. How can we achieve this by using forEach?
Without ForEach:
Map<String, Integer> map = new HashMap<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() + ", Value : " +entry.getValue());
}
With ForEach:
Map<String, Integer> map = new HashMap<>();
map.forEach((k, v) -> System.out.println("Key : " + k + ", Value : " + v));
2) Java 8 Streams API
Streams API was introduced in Java 8 by using which we can process, filter, or transform a set of collection data in any other required way. Streams API is part of Java. util package and has mechanisms named the filter, map and reduce to process the data from the collections.
How Streams API Works: To Work with streams, first we add collection data into a stream and then perform required actions such as filtering or mapping on the stream and then store the processed data in some other collection or use it for some other purpose. So the original collection data will remain intact.
Let’s take a scenario where we need to filter out all the links that contain specific text.
WebDriver driver= LocalDriverFactory.getDriver();
driver.get("https://www.freshworks.com/");
List<WebElement>el=driver.findElements(By.xpath("//a[contains(@href,'https://freshdesk.com/')]"));
el.stream().filter(item->item.getText().contains("freshwork"))
.collect(Collectors.toList()).forEach(item->System.out.println(item));
Let’s take another example, Where we need to perform certain tasks:
- Navigate to a shopping website
- Fetch all the links that contain anchor tags.
- Remove empty links.
- Remove the duplicates.
- Display text that contains either “Shoes” or “Watches”
Without Using Stream API(Java 7)
public void TestJava(){
FrameworkConfig config= ConfigFactory.create(FrameworkConfig.class);
WebDriver driver= LocalDriverFactory.getDriver();
driver.get("https://www.myntra.com/");
List<WebElement> el =driver.findElements(By.xpath("//a"));
List<String> text=new ArrayList<>();
for(int i=0;i<el.size();i++)
{
String temp=el.get(i).getText();
if(!temp.isBlank())
{
text.add(temp);
}
}
List<String> unique= new ArrayList<>(new HashSet<>(text));
for(int i=0;i<unique.size();i++)
{
if(unique.get(i).contains("Shoes")||unique.get(i).contains("Watches"))
{
System.out.println(unique.get(i));
}
}
By Using Stream API(Java 8)
el.stream().map(e->e.getText()).filter(s->!s.isBlank()).distinct()
.filter(s->s.contains("Shoes")||s.contains("Watches")).forEach(s->System.out.println(s));
The code looks much cleaner and more readable. Lines of code were also reduced.
Result:
3) Java Predicate
A predicate in general is a predefined functional interface that returns a boolean value. It is defined in Java. util.function package and contains different methods like isEqual(),negate(),and(),or() etc which helps in improving the code manageability. Java lambda expression simplifies the creation of Java predicates. Lambda expressions take in parameters and return values. In general, we can use predicate as per the below example.
1) Filter numbers whose values are less than 5.
public class JavaPredicate {
public static void main(String[] args) {
List<Integer> nums = List.of(2, 3, 1, 5, 6, 7, 8, 9, 12);
Predicate<Integer> btf = n -> n < 5;
nums.stream().filter(btf).forEach(System.out::println);
}
}
How can we utilize predicate in Test Automation? Suppose we have to validate certain conditions in our framework like after a certain period a web element is visible or not or an element is enabled or not. In such scenarios, we get either True or False and based on that, our script passes or fails.
1) Create a new class name as Validation and write a custom method that accepts the predicate as an argument and returns the expected result.
public class Validation {
private final BooleanSupplier condition;
private final long waitSeconds = 60;
public Validation(BooleanSupplier condition) {
this.condition = condition;
}
public static boolean waitFor(BooleanSupplier predicate) {
return new Validation(predicate).waitForConditionToBeMet();
}
2) Validate whether a certain field is visible on the webpage or not.
public void TestJava(){
FrameworkConfig config= ConfigFactory.create(FrameworkConfig.class);
WebDriver driver= LocalDriverFactory.getDriver();
driver.get("https://www.myntra.com/");
Assert.assertTrue(Validation.waitFor(() -> driver.findElement(By.xpath("//h4[normalize-space()='DEAL OF THE DAY']")).isDisplayed()));
}
4) Switch Expression in Java 12
We can now directly assign a value to a variable based on the result of the switch processing without using Break or default statements.
public static Response executeMethod(String endpoint, HttpMethod method){
Response response = switch(method){
case GET -> request.get(endpoint);
case POST -> request.post(endpoint);
case PUT -> request.put(endpoint);
case PATCH -> request.patch(endpoint);
case DELETE -> request.delete(endpoint);
}
response.then().log().all();
return response;
}
Apart from all these features, there are several new enhancements available with the latest version of Java. By using this we can improve the readability of our test automation code.
5) Pattern Matching Of InstanceOf in Java 14
The Instance of operator in Java is used to check if an instance belongs to a class, subclass or interface. In the traditional approach, we used to check the instanceOf operator followed by the cast.
import org.openqa.selenium.WebElement;
public class TestAutomationScript {
public void performAction(WebElement element) {
if (element instanceof WebElement) {
// Do something with the WebElement
System.out.println("This is a WebElement");
}
// Other actions...
}
}
From Java 14 we can combine the instanceOf operator with a cast, and Java will automatically cast the object to the desired type if the check passes. This helps to make code more concise and readable:
public void performAction(Object object) {
if (object instanceof WebElement element) {
System.out.println("This is a WebElement");
element.click(); // Example of using WebElement method
}
}
6) String templates of Java 21
String templates intend to simplify the process of string formatting and manipulation in Java. Refer to the example code prior to Java 21.
String productName = "Pen";
double productPrice = 10.00;
boolean productAvailable = true;
String productInfo = "Product: " + productName + "\nPrice: $" + productPrice + "\nAvailability: " + (productAvailable ? "In Stock" : "Out of Stock");
System.out.println(productInfo);
With Java 21 string templates we can simplify the formatting process and make the code more readable.
String productName = "Pen";
double productPrice = 10.00;
boolean productAvailable = true;
String productInfo = `Product: ${productName}
Price: $${productPrice}
Availability: ${productAvailable ? "In Stock" : "Out of Stock"}`;
System.out.println(productInfo);
Conclusion
Incorporating new Java features into test automation can significantly improve the efficiency, readability, and reliability of test suites. To learn more about the Java release and updates you can visit official documentation.