The Talent500 Blog
Mastering Test Automation With Design Patterns 1

Mastering Test Automation With Design Patterns

What are Design Patterns?

Design patterns are similar to a collection of guidelines that specify how to use different parts of code to address an issue. You may then apply the same pattern to handle related problems in the future, saving you from having to figure out how to address the issue from scratch each time.

To further understand, let’s examine a straightforward example.

Let’s say you have a box of toys and wish to play in different ways with them.You may desire to build a tower, a bridge, or a house on different occasions. You can now construct things whenever you want by using the same toys in various ways.For instance, you can use the same bricks you used to build a bridge to build a skyscraper by arranging them differently.

Similarly, design patterns are instructions for how to use various pieces of code to solve various problems. The same way you can use the same pieces of toys in different ways to solve different problems, you can use the same pieces of code in different ways to solve different problems.

Types of Design Patterns for Test Automation 

There are several types of design patterns that can be applied to automation testing. Here are some of the most common:

Page Object Model (POM): 

POM is a popular design pattern for UI/mobile automation testing. It involves creating a separate class for each page or screen in the mobile application, and encapsulating the page-specific logic in that class. This can help make tests more readable and maintainable, and reduce the risk of code duplication. POM is being used in almost all the Mobile Automation and Web Automation framework, as page class creation and mapping it with test scripts is the most appropriate approach for framework design.

Singleton: 

Singleton is a design pattern that involves creating a single instance of a class that can be used throughout the application. This can be useful in mobile/UI/API  automation testing for managing resources such as databases or network connections. This is the most common pattern which we usually find in the test automation framework design.

Factory: 

Factory is a design pattern that involves creating a separate class for creating objects. This can be useful in automation testing for creating objects such as test data or test objects. Though it is quite rarely used, still it comes handy if we implement it while designing the framework.

 

Builder: 

Builder is a design pattern that involves creating an object piece by piece. This can be useful in mobile/API/UI  automation testing for creating complex objects such as test scenarios or test cases. But mostly we will use Builder patterns in API Test Automation framework for Request Specification and Response Specification.

In the next section we will try to take two Design Pattern into consideration and explain the same with example and code snippet

Singleton Pattern Implementation for SoftAssertion

 

Here is an illustration of a SoftAssert utility class that uses the Singleton design pattern:

public class SoftAssertUtil {

    private static SoftAssert softAssertInstance;

    private SoftAssertUtil() {}

    public static SoftAssert getInstance() {

        if (softAssertInstance == null) {

            softAssertInstance = new SoftAssert();

        }

        return softAssertInstance;

    }

    public static void assertAll() {

        getInstance().assertAll();

    }

}

In this example, the SoftAssertUtil class is designed to provide a single instance of the SoftAssert class that can be used throughout an entire test suite. The getInstance() method creates a new SoftAssert instance if one does not already exist, and returns the existing instance if one does. The assertAll() method simply calls the assertAll() method on the SoftAssert instance, allowing all assertions made with the SoftAssert to be checked at once.

Simply call the SoftAssertUtil.getInstance() method to acquire the SoftAssert object and use it to build your assertions to leverage this utility class.Use SoftAssertUtil.assertAll() to verify every assertion that was created using the SoftAssert when you are ready to do so. By using this method, you can guarantee that there is only one instance of SoftAssert used throughout all of your tests, which eliminates any potential problems with inconsistent or contradictory assertion results.

 

public class SoftAssertUtil {

    private static SoftAssert softAssertInstance;

    private SoftAssertUtil() {}

    public static SoftAssert getInstance() {
        if (softAssertInstance == null) {
            softAssertInstance = new SoftAssert();
        }
        return softAssertInstance;
    }

    public static void assertAll() {
        getInstance().assertAll();
    }

    public static void assertTrue(boolean condition, String message) {
        getInstance().assertTrue(condition, message);
    }
}

 

In this updated example, the assertTrue() method is added to the SoftAssertUtil class. The assertTrue() method takes two parameters: a boolean condition and a message. The method then calls the assertTrue() method on the SoftAssert instance with the same parameters. This allows you to make assertions using the SoftAssert instance and customize the error message.

Simply call SoftAssertUtil.assertTrue(condition, message) to utilize the assertTrue() method, specifying the condition you wish to assert and the message you want to show if the assertion fails. Make the same call to SoftAssertUtil.assertAll() when you are ready to verify all of your assertions.In addition to the current soft assertions offered by SoftAssert, you can now also make boolean assertions using this new method.

 

Builder Pattern Implementation in API Automation

To fully comprehend this part, let’s attempt implementing the solution using the Normal Method first, and then we’ll use the Builder Pattern to demonstrate the advantages of adopting the Builder Pattern in place of the Normal Approach.

Normal approach:

 

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;

public class RestAssuredNormalApproachTest {

    @Test
    public void testRestAssuredNormalApproach() {
        RestAssured.baseURI = “https://jsonplaceholder.typicode.com”;

        given()
                .contentType(ContentType.JSON)
                .queryParam(“userId”, “1”)
                .header(“Authorization”, “Bearer my-token”)
                .when()
                .get(“/posts”)
                .then()
                .assertThat()
                .statusCode(200);
    }
}

 

Using the Builder pattern:

 

import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;

public class RestAssuredBuilderPatternTest {

    private RequestSpecification requestSpec;

    @Test
    public void testRestAssuredBuilderPattern() {
        requestSpec = new RequestSpecBuilder()
                .setBaseUri(“https://jsonplaceholder.typicode.com”)
                .setContentType(“application/json”)
                .addQueryParam(“userId”, “1”)
                .addHeader(“Authorization”, “Bearer my-token”)
                .build();

        given()
                .spec(requestSpec)
                .when()
                .get(“/posts”)
                .then()
                .assertThat()
                .statusCode(200);
    }
}

 

Benefits with Example:

Let’s try to understand the benefits by taking the example of Builder Pattern implementation in Rest Assured Framework for API.

Here are some differences between the two approaches:

1. Readability: 

It could be challenging to examine every aspect of the request using the standard method, including the base URL, headers, query parameters, etc. The Builder pattern makes it simpler to comprehend the request and what it accomplishes by allowing you to examine all the request’s specifics in one location and in great detail.

 

2. Maintainability: 

As the API changes, you may need to modify the request to accommodate these changes. With the normal approach, modifying the request can be time-consuming, as you need to modify the request in multiple places. With the Builder pattern, you only need to modify the request in one place, which makes it easier to maintain the request over time.

 

3. Reusability: 

With the normal approach, it can be difficult to reuse the request in other tests. With the Builder pattern, you can create a RequestSpecification instance once and reuse it across multiple tests. This can save time and effort when writing and maintaining test scripts.

Overall, using the Builder pattern in Rest Assured framework can lead to more readable, maintainable, and reusable code.

Conclusion:

Finally, incorporating design patterns into a framework for automating tests can have a variety of advantages, such as code reuse, maintainability, scalability, adherence to best practices, and consistency.

By employing design patterns to write code that is simpler to read, comprehend, and edit, developers can decrease the time and effort needed for test creation and maintenance.

 

1+
Sidharth Shukla

Sidharth Shukla

Currently working as a SDET. He is an Automation enabler who provides solutions that mitigates quality risk. Passionate about technical writing and contribution towards QA community.

Add comment