The Builder Pattern in Java: Guide to Test Data Generation
In this tutorial, learn how to use the Builder pattern in Java with a Datafaker library to generate test data for automated testing.
Join the DZone community and get the full member experience.
Join For FreeWhile automating API, web, or mobile applications that require user registration for automated testing, you typically set the address for a product in the end-to-end user journey.
So, how do you do that?
Normally, we create a POJO class in Java with the fields required to register a user or to set the address for checking out the product. Then, we set the values in the test using the constructor of the POJO class.
Let’s take a look at an example scenario where registering a user incorporates the following mandatory fields to fill in the registration form:
- First Name
- Last Name
- Address
- City
- State
- Country
- Mobile Number
To handle these fields in automation testing, we will have to pass on respective values in the fields at the time of executing the tests.
Before Using the Builder Design Pattern
A POJO class, with the above-mentioned mandatory fields, will be created with the Getter and Setter methods inside that POJO class, using the constructor value set in the respective fields.
Check out the code example of RegisterUser class
below:
public class RegisterUser {
private String firstName;
private String lastName;
private String address;
private String city;
private String state;
private String country;
private String mobileNumber;
public RegisterUser (final String firstName, final String lastName, final String address, final String city,
final String state, final String country, final String mobileNumber) {
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.city = city;
this.state = state;
this.country = country;
this.mobileNumber = mobileNumber;
}
public String getFirstName () {
return firstName;
}
public void setFirstName (final String firstName) {
this.firstName = firstName;
}
public String getLastName () {
return lastName;
}
public void setLastName (final String lastName) {
this.lastName = lastName;
}
public String getAddress () {
return address;
}
public void setAddress (final String address) {
this.address = address;
}
public String getCity () {
return city;
}
public void setCity (final String city) {
this.city = city;
}
public String getState () {
return state;
}
public void setState (final String state) {
this.state = state;
}
public String getCountry () {
return country;
}
public void setCountry (final String country) {
this.country = country;
}
public String getMobileNumber () {
return mobileNumber;
}
public void setMobileNumber (final String mobileNumber) {
this.mobileNumber = mobileNumber;
}
}
Now, if we want to use this POJO, we would have to create an instance of RegisterUser
class and pass the values in the constructor parameters
as given in the code example below to set the data in the respective fields.
Check out the following example of the Register User
test:
public class RegistrationTest {
@Test
public void testRegisterUser () {
RegisterUser registerUser = new RegisterUser ("John", "Doe", "302, Adam Street, 1st Lane", "New Orleans",
"New Jersey", "US", "52145364");
assertEquals (registerUser.getFirstName (), "John");
assertEquals (registerUser.getCountry (), "US");
}
}
There were just seven fields in the example we took for registering the user. However, this would not be the case with every application. There would be additional fields required, and as the fields increase each time, we would need to update the POJO class with respective Getter and Setter methods as well as update the parameters in the constructor. Finally, we would need to add the values to those fields so that the data is passed in the required field.
Long story short, we would need to update the code even if there is a single new field added. Additionally, it doesn’t look clean to add values as parameters in the tests. Luckily, the Builder pattern in Java comes to the rescue here.
Find the following example code on GitHub here.
What Is the Builder Pattern in Java?
The Builder design pattern is a creational pattern in Java that lets you construct complex objects step by step. This pattern allows you to produce different types and representations of an object using the same construction code.
The Builder pattern in Java also helps us solve the issue of setting parameters by providing a way to build the objects step by step, providing a method that returns a final object which can be used in the actual tests.
What Is Lombok?
Project Lombok is a Java library that automatically plugs into your editor and builds tools, spicing up your Java code. It is an annotation-based Java library that helps reduce boilerplate code.
Project Lombok also assists us in:
- Writing short, crisp code without having to write boilerplate code.
- Bypassing the
@Getter
annotation over the class, it automatically generates Getter methods. - Similarly, you don’t have to write the code for Setter methods as its
@Setter
annotation updated over the class automatically generates the Setter methods.
Project Lombok supports the Builder pattern in Java, so we just need to put the @Builder
annotation above the class — and the rest is taken care of by the Lombok library.
To use Lombok annotations in the project, we need to add the following Maven dependency:
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.32</version> <scope>provided</scope> </dependency>
Using the Builder Design Pattern With Lombok
Before we start refactoring our code, let's discuss the Datafaker library as well as how it helps generate fake data that can be used for testing. Ideally, in our example, every newly registered user’s data should be unique. Otherwise, we may get a duplicate data error and the test will fail.
Here, the Datafaker library will help us in providing unique data for each test execution, thereby helping us to register a new user with unique data every time the registration test is run.
To use the Datafaker library, we need to add the following Maven dependency to our project:
<!-- https://mvnrepository.com/artifact/net.datafaker/datafaker -->
<dependency>
<groupId>net.datafaker</groupId>
<artifactId>datafaker</artifactId>
<version>2.2.2</version>
</dependency>
Now, let's start refactoring the code. First, we will make changes to the RegisterUser
class. We would be removing all Getter, Setter, and constructor methods, adding the @Getter
and @Builder
annotation tags on the top of the RegisterUser class
.
Here is how the RegisterUser class
looks after the refactoring:
@Getter
@Builder
public class RegisterUserWithBuilder {
private String firstName;
private String lastName;
private String address;
private String city;
private String state;
private String country;
private String mobileNumber;
}
Check out how clean and crisp it looks with that refactoring done! Multiple lines of code are removed, yet it still works in the same fashion as shown previously, thanks to Lombok.
We would need to add a new Java class for generating the fake data on runtime using the Builder design pattern, thus calling this the DataBuilder class
.
public class DataBuilder {
private static final Faker FAKER = new Faker();
public static RegisterUserWithBuilder getUserData () {
return RegisterUserWithBuilder.builder ()
.firstName (FAKER.name ()
.firstName ())
.lastName (FAKER.name ()
.lastName ())
.address (FAKER.address ()
.streetAddress ())
.state (FAKER.address ()
.state ())
.city (FAKER.address ()
.city ())
.country (FAKER.address ()
.country ())
.mobileNumber (String.valueOf (FAKER.number ()
.numberBetween (9990000000L, 9999999999L)))
.build ();
}
}
The getUserData()
method will return the test data required for registering the user using the Datafaker library. Notice the builder()
method used after the class name RegisterUserWithBuilder
. It appears because of the @Builder
annotation we used on top of the RegisterUserWithBuilder class.
After the builder()
method, we need to pass the variables declared in the RegisterUserWithBuilder class
as well as the fake data needed for its respective variables.
RegisterUserWithBuilder.builder ()
.firstName (FAKER.name ()
.firstName ());
The above piece of code will generate a fake first name and set it in the First Name variable. Likewise, we have set fake data in all other variables.
Now, let’s move towards how we use these data in the tests. It's quite simple: the below code snippet demonstrates it all:
@Test
public void testRegisterUserWithBuilder () {
RegisterUserWithBuilder registerUserWithBuilder = getUserData ();
System.out.println (registerUserWithBuilder.getFirstName ());
System.out.println (registerUserWithBuilder.getLastName ());
System.out.println (registerUserWithBuilder.getAddress ());
System.out.println (registerUserWithBuilder.getCity ());
System.out.println (registerUserWithBuilder.getState ());
System.out.println (registerUserWithBuilder.getCountry ());
System.out.println (registerUserWithBuilder.getMobileNumber ());
}
We just need to call the getUserData()
method while instantiating the RegisterUserWithBuilder class
. Next, we call the Getter methods for the respective variables we declared inside the RegisterUserWithBuilder class
.
Remember: We passed the @Getter
annotation on top of the RegisterUserWithBuilder class
. This actually helps in calling the Getter methods here.
Also, we are not required to pass on multiple data as the constructor parameters for the RegisterUserWithBuilder
class; instead, we just need to instantiate the class and call the getUserData()
method!
Thanks to the Builder design pattern and Project Lombok, it is quite easy to generate the unique data for automated tests without having to write multiple lines of boilerplate code!
Running the Test
Let’s run the test and check if the user details are printed in the console:
Hooray! We can see that the fake data is generated successfully in the screenshot above of the test execution.
Conclusion
In this article, we discussed making use of the Builder design pattern in Java with Project Lombok and the Datafaker library to generate test data at runtime and use it in our automated tests. This eases the test data generation process by eliminating the need to update the test data before running each test.
I hope this tutorial helps in reducing your lines of code and writing cleaner code.
Happy testing!
Published at DZone with permission of Faisal Khatri. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments