Chapter 91. Using Object-Oriented Principles in Test Code

Angie Jones

When writing test code, it’s important to exercise the same care that you’d use when developing production code. Here are common ways to use object-oriented (OO) principles when implementing test code.

Encapsulation

The Page Object Model design pattern is commonly used in test automation. This pattern prescribes creating a class to interact with a page of the application under test. Within this class are the locator objects for the elements of the web page and the methods to interact with those elements.

It’s best to properly encapsulate by restricting access to the locators themselves and only exposing their corresponding methods:

public class SearchPage {
    private WebDriver driver;
    private By searchButton = By.id("searchButton");
    private By queryField = By.id("query");
   
    public SearchPage(WebDriver driver){
        this.driver = driver;
    }
   
    public void search(String query) {
        driver.findElement(queryField).sendKeys(query);
        driver.findElement(searchButton).click();
    }
}

Inheritance

While inheritance should not be abused, it can certainly be useful in test code. For example, given there are header and footer components that exist on every page, it’s redundant to create fields and methods for interacting with these components within every Page Object class. Instead, create a base Page class containing the common members that exist on every page, and have your Page Object classes inherit from this class. Your test code will now have access to anything in the header and footer no matter what Page Object they are currently interacting with.

Another good use case for inheritance within test code is when a given page has various implementations. For example, your app may contain a User Profile page that has different functionality based on roles (e.g., Administrator, Member). While there are differences, there could also be overlap. Duplicating code across two classes is not ideal. Instead, create a ProfilePage class that contains the common elements/interactions, and create subclasses (e.g., AdminProfilePage, MemberProfilePage) that implement the unique interactions and inherit the common ones.

Polymorphism

Assume we have a convenience method that goes to the User Profile page. This method doesn’t know what type of profile page it is—an Administrator or a Member.

You’re faced with a design decision here. Do you make two methods—one for each of the profile types? This seems like overkill since they both would do the exact same thing but just have a different return type.

Instead, return the superclass (ProfilePage) since both AdminProfilePage and MemberProfilePage are both subclasses of ProfilePage. The test method that is calling this convenience method has more context and can cast accordingly:

@Test
public void badge_exists_on_admin_profile() {
    var adminProfile = (AdminProfilePage)page.goToProfile("@admin");
    ...
}

Abstraction

Abstraction is used sparingly in test code, but there are valid use cases. Consider a type of widget that has been customized for different usages throughout the app. Creating an abstract class that specifies the behaviors expected is helpful when developing classes that interact with specific implementations of that widget:

public abstract class ListWidget {
    protected abstract List<WebElement> getItems();
    int getNumberOfItems() {
        return getItems().size();
    }
}

public class ProductList extends ListWidget {
    private By productLocator = By.cssSelector(".product-item");
    @Override
    protected List<WebElement> getItems() {
        return driver.findElements(productLocator);
    }
}

Test code is indeed code, meaning that it has to be maintained, enhanced, and scaled. Therefore, it’s in your best interest to follow good programming practices when developing it—including the foundational OO principles.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.218.230