Monday, March 28, 2022

Project Lombok -Helper Libray for Test Automation Framework

Selenium Test Framework and Java 

We all know that Selenium is a widely used tool for cross-browser web automation testing. 

It Supports multiple programming languages for clients, and Java is one of the most widely used language bindings to build the Test Automation framework using Selenium Webdriver.


While implementing Page Object Model or Handling a large set of data for Test Automation, writing Data Object Class in Java with lots of fields and getter setter is a hectic task for any developer. And also, initializing multiple objects with a different set of data creates boilerplate codes which hard to maintain. 


What is Project Lombok


Project Lombok is a java library that makes writing java code less tedious.


It has several annotations, including @Getter and @Setter, which help you automatically generate your class's getters and setters. And it has a convenient annotation, @Builder, which helps you generate a class with a builder for configuring its properties.



What are the benefits of Lombok library?

Of course, every library comes with many benefits to our code, and Lombok is yet another one that gives us some advantages:


  • Open Source Library

  • It saves time for writing standard boilerplate code

  • Reduce 1/10th of Lines of Code

  • Making code cleaner than before



How to add Lombok into IntelliJ IDEA and Maven Project



Adding IntelliJ Plugin


Here I am using Mac Machine with IntelliJ IDEA. In Selenium, TestNG, Maven project, let me explain how to integrate the Lombok library.


As of IntelliJ version 2020.3, we don't need to configure the IDE to use Lombok anymore. Instead, the IDE comes bundled with the plugin. Also, the annotation processing will be enabled automatically.



Make sure these mentioned options are enabled for seamless use of Lombok annotations.





If you are using older versions of IntelliJ IDEA, navigate to IntelliJ. 


  • Go to IntelliJ IDEA-> Preferences> Plugins

  • Click on Browse repositories

  • Search for Lombok Plugin

  • Click on Install plugin

  • Restart IntelliJ IDEA


Adding Lombok to the Maven project 


Search for the Lombok maven plugin in the maven central repo and add that to your current project pom.xml file 


Now the project is ready to use Lombok annotation and just escape from boilerplate codes.

Simple POJO Object without Lombok


Consider an Employee Class with empName,empCode, and joining Date as private fields with respective getter, setter, and constructor.


package test.java.my.codeSamples;

 

import java.util.Date;

import java.util.Objects;

 

public class EmployeeData {

private String empName;

private String emailId;

private int empCode;

private Date joiningDate;

 

           public EmployeeData(){

  

     }

  

            public EmployeeDataOld(String empName

                         String emailId, int empCode, Date joiningDate) {

    this.empName = empName;

    this.emailId = emailId;

    this.empCode = empCode;

    this.joiningDate = joiningDate;

            }

 

 

public String getEmpName() {

     return empName;

}

 

public void setEmpName(String empName) {

     this.empName = empName;

}

 

public String getEmailId() {

     return emailId;

}

 

public void setEmailId(String emailId) {

     this.emailId = emailId;

}

 

public int getEmpCode() {

     return empCode;

}

 

public void setEmpCode(int empCode) {

     this.empCode = empCode;

}

 

public Date getJoiningDate() {

     return joiningDate;

}

 

public void setJoiningDate(Date joiningDate) {

     this.joiningDate = joiningDate;

}

 

}

 


The above class has 55 + lines of code just for a few data variables. This class may be hectic to manage when the actual class needs to hold a large set of variables and getters and setters for those. Moreover, if I  want to include the toString override method, it may increase another six lines of code, altogether  sixty+ lines.


To access this Class variables and method, I  need to create Objects for this class.


 

EmployeeData employeeData= new EmployeeData();

 

employeeData.setEmpName("John");

 

System.out.println("newlyAdded "+ employeeData.getEmpName());




To overcome all these pain points like too many getters and setters, toString methods, constructors, the Lombok library provides various annotations.



Lombok Annotations and implementations


@Getter and @Setter Annotations


Adding Getter and setter annotations on the class level generate public accessors for all fields.



 

@Getter

@Setter

public class EmployeeData {

   private String empName;

   private String emailId;

   private int empCode;

   private Date joiningDate;

 

}

 




If I want to omit the getter or setter for any fields, then I can set AccessLevel.NONE  for that specific field.





 

    @Getter

    @Setter

    public class EmployeeData {

   private String empName;

   private String emailId;

   private int empCode;

 

  @Setter(AccessLevel.NONE)

   private Date joiningDate;

 

 

  public EmployeeData() {

 

   }

 

   public EmployeeData(String empName, String emailId, int empCode, Date joiningDate) {

       super();

       this.empName = empName;

       this.emailId = emailId;

       this.empCode = empCode;

       this.joiningDate = joiningDate;

   }

 }

 

    }

 



The above code skips setJoiningDate method while generating source code.

 .

AccessLevel is an Enum class with NONE, PRIVATE, PUBLIC, and MODULE to define access for that field’s getter and setter.



@Builder Annotation 


Lombok @Builder annotations help implement the Builder design pattern to handle the creation of complex objects of a given class.

We can use Builder annotation at both the class and method levels.




In the below class, I added @Builder annotation for class EmployeeData.


 

    @Getter

    @Setter

    @Builder

   public class EmployeeData {

   private String empName;

   private String emailId;

   private int empCode;

 

  @Setter(AccessLevel.NONE)

   private Date joiningDate;

 

   public EmployeeData() {

 

   }

 

   public EmployeeData(String empName, String emailId, int empCode, Date joiningDate) {

       super();

       this.empName = empName;

       this.emailId = emailId;

       this.empCode = empCode;

       this.joiningDate = joiningDate;

   }

 }

 

    }

 




Now to access this method in the class, I dont need to use the traditional way of the new keyword.


 

  EmployeeData employeeData=EmployeeData.builder().empCode(104)

       .emailId("test@lambdatest.com")

       .empName("john")

       .empCode(001)

       .build();

 

 System.out.println("newlyAdded "+ employeeData.getEmpName());

 System.out.println("newlyAdded "+ employeeData.getEmpCode());

 System.out.println("newlyAdded "+ employeeData.getEmailId());

 

 


Creating objects and assigning values using a setter can be done in a single line.



@Data Annotation


When I dont need any access level customization of field level getters and setters, then I can use  @Data annotation.



@AllArgsConstructor &  @NoArgsConstructor Annotations


Lombok provides a few other annotations to simplify the creation of constructions.


To create a constructor with all default fields can @AllArgsConstructor.


If I need to include the default constructor, then @NoArgsConstructor



@Data

@Builder

@AllArgsConstructor

@NoArgsConstructor

public class EmployeeData {

   private String empName;

   private String emailId;

   private int empCode;

   private Date joiningDate;

 

}

 



Here is the class file to create objects with 


 

public class TestEmployee {

 

  public static void main(String args[]){

 

      EmployeeData newMEp= EmployeeData.builder().build();

 

      EmployeeData employeeData=EmployeeData.builder().empCode(104)

              .emailId("test@lambdatest.com")

              .empName("john")

              .empCode(001)

              .build();

 

      EmployeeData employeeData1=EmployeeData.builder().empCode(104)

              .emailId("test@lambdatest.com")

              .empName("john")

              .empCode(001)

              .build();

 

      System.out.println("newlyAdded "+ employeeData.getEmpName());

      System.out.println("newlyAdded "+ employeeData.getEmpCode());

      System.out.println("newlyAdded "+ employeeData.getEmailId());

      System.out.println("newlyAdded "+ employeeData.getEmailId());

 

      System.out.println(employeeData1.equals(employeeData));

      System.out.println(employeeData1.toString());

 

  }

}

 





Using Delombok


Even though the Lombok library helps with code maintenance, there are a few drawbacks that developers hesitate to implement this library. For example, missing Java doc supports, cannot view the call hierarchy of getter and setter of the field. 


So Delombok can help you understand what Lombok is doing 'under the hood,’ so you can examine it, change it or delete it.


 

After delombok, the source code to see Lombok generated source code, which does not affect the class which instantiated object of this class.

public class EmployeeData {

   private String empName;

   private String emailId;

   private int empCode;

   private Date joiningDate;

 

   public EmployeeData(String empName, String emailId, int empCode, Date joiningDate) {

       this.empName = empName;

       this.emailId = emailId;

       this.empCode = empCode;

       this.joiningDate = joiningDate;

   }

 

   public EmployeeData() {

   }

 

   public static EmployeeDataBuilder builder() {

       return new EmployeeDataBuilder();

   }

 

   public String getEmpName() {

       return this.empName;

   }

 

   public String getEmailId() {

       return this.emailId;

   }

 

   public int getEmpCode() {

       return this.empCode;

   }

 

   public Date getJoiningDate() {

       return this.joiningDate;

   }

 

   public void setEmpName(String empName) {

       this.empName = empName;

   }

 

   public void setEmailId(String emailId) {

       this.emailId = emailId;

   }

 

   public void setEmpCode(int empCode) {

       this.empCode = empCode;

   }

 

   public void setJoiningDate(Date joiningDate) {

       this.joiningDate = joiningDate;

   }

 

   public boolean equals(final Object o) {

       if (o == this) return true;

       if (!(o instanceof EmployeeData)) return false;

       final EmployeeData other = (EmployeeData) o;

       if (!other.canEqual((Object) this)) return false;

       final Object this$empName = this.getEmpName();

       final Object other$empName = other.getEmpName();

       if (this$empName == null ? other$empName != null : !this$empName.equals(other$empName)) return false;

       final Object this$emailId = this.getEmailId();

       final Object other$emailId = other.getEmailId();

       if (this$emailId == null ? other$emailId != null : !this$emailId.equals(other$emailId)) return false;

       if (this.getEmpCode() != other.getEmpCode()) return false;

       final Object this$joiningDate = this.getJoiningDate();

       final Object other$joiningDate = other.getJoiningDate();

       if (this$joiningDate == null ? other$joiningDate != null : !this$joiningDate.equals(other$joiningDate))

           return false;

       return true;

   }

 

   protected boolean canEqual(final Object other) {

       return other instanceof EmployeeData;

   }

 

   public int hashCode() {

       final int PRIME = 59;

       int result = 1;

       final Object $empName = this.getEmpName();

       result = result * PRIME + ($empName == null ? 43 : $empName.hashCode());

       final Object $emailId = this.getEmailId();

       result = result * PRIME + ($emailId == null ? 43 : $emailId.hashCode());

       result = result * PRIME + this.getEmpCode();

       final Object $joiningDate = this.getJoiningDate();

       result = result * PRIME + ($joiningDate == null ? 43 : $joiningDate.hashCode());

       return result;

   }

 

   public String toString() {

       return "EmployeeData(empName=" + this.getEmpName() + ", emailId=" + this.getEmailId() + ", empCode=" + this.getEmpCode() + ", joiningDate=" + this.getJoiningDate() + ")";

   }

 

   public static class EmployeeDataBuilder {

       private String empName;

       private String emailId;

       private int empCode;

       private Date joiningDate;

 

       EmployeeDataBuilder() {

       }

 

       public EmployeeDataBuilder empName(String empName) {

           this.empName = empName;

           return this;

       }

 

       public EmployeeDataBuilder emailId(String emailId) {

           this.emailId = emailId;

           return this;

       }

 

       public EmployeeDataBuilder empCode(int empCode) {

           this.empCode = empCode;

           return this;

       }

 

       public EmployeeDataBuilder joiningDate(Date joiningDate) {

           this.joiningDate = joiningDate;

           return this;

       }

 

       public EmployeeData build() {

           return new EmployeeData(empName, emailId, empCode, joiningDate);

       }

 

       public String toString() {

           return "EmployeeData.EmployeeDataBuilder(empName=" + this.empName + ", emailId=" + this.emailId + ", empCode=" + this.empCode + ", joiningDate=" + this.joiningDate + ")";

       }

   }

}

 

 

Conclusion

Lombok is an excellent tool that makes our code more elegant and less code. But we need to know how to use it intelligently.

In this article, I have covered the most common features of Lombok that are useful to build a Test Automation Framework. Feel free to explore the rest of Lombok's features here.

 

 

 


Happy Testing!

 

 

 

 

 






No comments:

Post a Comment