UI Automation Patterns and Methodologies: Real Life Examples

Software development and software testing at first glance seem very different from each other, but there are aspects that are important in both disciplines.





In this article, we'll look at some of the common software design patterns and methodologies that can be useful when working with UI automation, especially when building a UI testing framework. The examples and use cases provided here are for our own framework.





Please note that these are not complete code examples, but rather simplified code examples that illustrate concepts. Since this is my main programming language, the code below is written in Java. I tried to make it as simple and straightforward as possible.





Useful Patterns for Automating UI Testing

A design pattern is an incomplete piece of code that can be used in projects. On the contrary, it looks more like a solution plan.





, . , , , .





(Decorator)

-. , - , A/B- .





, ! «», . : . , - .





Login. «».





- . , «». . , , , .









LoginComponent - Login. , login.





package decorator;

public interface LoginComponent {
   void login(String user, String password);
}
      
      



BasicLoginComponent login. «Basic login» .





package decorator;

public class BasicLoginComponent implements LoginComponent {
   @Override
   public void login(String user, String password) {
       System.out.println("Basic login: " + user + ", " + password);
   }
}
      
      



. LoginDecorator LoginComponent . LoginComponent.





package decorator;

public abstract class LoginDecorator implements LoginComponent {
   private final LoginComponent loginComponent;
   public LoginDecorator(LoginComponent loginComponent) {
       this.loginComponent = loginComponent;
   }
   @Override
   public void login(String user, String password) {
       loginComponent.login(user, password);
   }
}
      
      



MobileLoginDecorator login , . , «Mobile login», .





package decorator;

public class MobileLoginDecorator extends LoginDecorator {
   public MobileLoginDecorator(LoginComponent loginComponent) {
       super(loginComponent);
   }
   @Override
   public void login(String user, String password) {
       System.out.println("Mobile login: " + user + ", " + password);
   }
}
      
      



CancelButtonDecorator cancel Login.





package decorator;

public class CancelButtonDecorator extends LoginDecorator {
   public CancelButtonDecorator(LoginComponent loginComponent) {
       super(loginComponent);
   }
   public void cancel() {
       System.out.println("Click the cancel button");
   }
}
      
      



, !





package decorator;

public class Main {
   public static void main(String[] args) {
   System.out.println("DECORATOR PATTERN");
   System.out.println("=================");

   // This is the basic login component
   LoginComponent loginComponent = new BasicLoginComponent();
   loginComponent.login("User", "PW");

   // Let's turn it into a mobile login component.
   loginComponent = new MobileLoginDecorator(loginComponent);
   loginComponent.login("User", "PW");

   // Finally, we can add a cancel functionality.
   loginComponent = new CancelButtonDecorator(loginComponent);
   ((CancelButtonDecorator) loginComponent).cancel();

   }
}
      
      



:





DECORATOR PATTERN
=================
Basic login: User, PW
Mobile login: User, PW
Click the cancel button
      
      



, , . , . .





Page Object Page Component

, UI, Page Object. , . , .





, , . : Page Component. , , .





Page Object

-, . Page Object, , WebshopPage.





package pageobjects;

public class WebshopPage {
   public void search(final String queryString) {
       System.out.println("Enter " + queryString);
       System.out.println("Click search button");
   }
   public void checkResultHeadline() {
       System.out.println("Check if the headline is correct.");
   }
   public void checkResults() {
       System.out.println("Check if there are search results.");
   }
}

      
      



, , . Main .





package pageobjects;

public class Main {
   public static void main(String[] args) {
   System.out.println("PAGE OBJECTS");
   System.out.println("============");

   WebshopPage webshopPage = new WebshopPage();
   webshopPage.search("T-Shirt");
   webshopPage.checkResultHeadline();
   webshopPage.checkResults();

   }
}

      
      



, :





PAGE OBJECTS
============
Enter T-Shirt
Click search button
Check if the headline is correct.
Check if there are search results.

      
      



. , .





Page Component

Page Component. : .





SearchBar .





package pagecomponents;
public class SearchBar {
   public void search(final String queryString) {
       System.out.println("Enter " + queryString);
       System.out.println("Click search button");
   }
}

      
      



ResultList:





package pagecomponents;
public class ResultList {
   public void checkResultHeadline() {
       System.out.println("Check if the headline is correct.");
   }
   public void checkResults() {
       System.out.println("Check if there are search results.");
   }
}

      
      



WebshopPage, .





package pagecomponents;
public class WebshopPage {
   public SearchBar searchBar() {
       return new SearchBar();
   }
   public ResultList resultList() {
       return new ResultList();
   }
}

      
      



. , .





package pagecomponents;
public class Main {
   public static void main(String[] args) {
   System.out.println("PAGE COMPONENTS");
   System.out.println("===============");

   WebshopPage webshopPage = new WebshopPage();
   webshopPage.searchBar().search("T-Shirt");
   webshopPage.resultList().checkResultHeadline();
   webshopPage.resultList().checkResults();

   }
}

      
      



:





PAGE COMPONENTS
===============
Enter T-Shirt
Click search button
Check if the headline is correct.
Check if there are search results.

      
      



, , . , .





(Factory)

, , , . , .





. . , .





.





-, , , . , . , .





package factory;
public class Component {
   public void initialize() {
       System.out.println("Initializing " + getClass().getName());
   }
}

      
      



Component:





public class ResultList extends Component {
    ...
}
public class SearchBar extends Component {
    ...
}

      
      



- , . , , initialize .





package factory;
public class ComponentFactory {
   public static Component getComponent(final String componentName) throws Exception {
   System.out.println("Creating " + componentName + "...");

   // Create a component instance for the passed in component name.
   Component component;
   switch (componentName){
       case "SearchBar":
           component = new SearchBar();
           break;
       case "ResultList":
           component = new ResultList();
           break;
       default:
           throw new Exception(componentName + " unknown.");
   }
   System.out.println("Component created: " + component);
   component.initialize();
   return component;

   }
}

      
      



Main , WebshopPage - .





package factory;
public class Main {
   public static void main(String[] args) throws Exception {
   System.out.println("FACTORY PATTERN");
   System.out.println("===============");

   WebshopPage webshopPage = new WebshopPage();
   webshopPage.searchBar().search("Berlin");

   }
}

      
      



:





FACTORY PATTERN
===============
Creating SearchBar...
Component created: factory.SearchBar@3d075dc0
Initializing factory.SearchBar
Enter Berlin
Click search button

      
      



, .





, . , , , .





(Dependency Injection)

« ». , , , , . .





, , , - , Spring Guice, . , .





UI -. , , . , , -. Login, . «» - .





LoginData, . .





package dependencyinjection;
public interface LoginData {
   String getUserName();
   String getPassword();
}

      
      



: «», «» .





package dependencyinjection;
public class LoginDataReal implements LoginData {
   @Override
   public String getUserName() {
       return "Real user";
   }
   @Override
   public String getPassword() {
       return "Real password";
   }
}
package dependencyinjection;
public class LoginDataFake implements LoginData {
   @Override
   public String getUserName() {
       return "Fake user";
   }
   @Override
   public String getPassword() {
       return "Fake password";
   }
}

      
      



LoginPage LoginData login. , LoginPage, .





package dependencyinjection;
public class LoginPage {
   private final LoginData loginData;
   public LoginPage(final LoginData loginData) {
       this.loginData = loginData;
   }
   public void login(){
       System.out.println("Logging in with " + loginData.getClass());
       System.out.println("- user: " + loginData.getUserName());
       System.out.println("- password: " + loginData.getPassword());
   }
}

      
      



- , .





package dependencyinjection;
public class Main {
   public static void main(String[] args) {
   System.out.println("DEPENDENCY INJECTION");
   System.out.println("====================");

   LoginPage loginPageReal = new LoginPage(new LoginDataReal());
   loginPageReal.login();

   LoginPage loginPageFake = new LoginPage(new LoginDataFake());
   loginPageFake.login();

   }
}

      
      



, . :





DEPENDENCY INJECTION
====================
Logging in with class dependencyinjection.LoginDataReal

user: Real user
password: Real password
Logging in with class dependencyinjection.LoginDataFake
user: Fake user
password: Fake password

      
      



, , , . , .





, , - . , , .





:

. , - : !





, UI, , . . , .





(Keep It Simple)

. , . , . , - .





(You Aren’t Gonna Need It)

, YAGNI, . « » , , . , , , , , , - .





If you stick to this principle, you'll probably develop your test framework faster because you don't have to think about all the possibilities before implementing. Plus, you can provide exactly the tools you need on demand, rather than providing an out-of-the-box solution that can be overwhelming and difficult to use.





Translated by the QApedia team. You will find even more translated articles on our telegram channel .








All Articles