Inheritance And How To Implement It In Automation Framework.

Deepak Attri
CodeX
Published in
5 min readMar 28, 2021

Inheritance in Java: A Brief

One of the core principles of Object-Oriented Programming — inheritance — enables us to reuse existing code or extend an existing type. Simply put, in Java, a class can inherit another class and multiple interfaces, while an interface can inherit other interfaces. When you inherit from an existing class, you can reuse methods and fields of the parent class. Moreover, you can add new methods and fields to your current class also.

Inheritance represents the IS-A relationship or a parent-child relationship. Consider a parent class Animal and child class Dog.

public class Animal {
public void move(){
System.out.println("Animals can move");
}

public void sleep(){
System.out.println("Animals can sleep");
}
}

Dog class is a child class of Animal.

public class Dog extends Animal {
@Override
public void move() {
System.out.println("Dogs can move");
}

public void bark() {
System.out.println("Dogs can bark");
}
}

Dog class will have access to all the methods of Animal except private members. You can use an object of Dog class to access the methods of Animal class. Refer to this document for more details.

Implementing Inheritance in Test Automation Framework

In order to build a robust test automation framework, using the OOPs concept is a must and inheritance is one of the core OOPs principles. Let’s see why, where and how can we use inheritance in a test automation framework.

The Base Class

Every test class extends to the Base class. In the base class, we initialise Webdriver, some variables and properties file. We even have TestNg beforeTest, afterTest, beforeMethod and afterMethod. A sample of Base Class is given below:

package Android;

import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;

import java.util.HashMap;
import java.util.Map;

public class Base {

public static String userPhone = "1234567890";
public static String userEmail = "xx.xxxxx@xxxx.com";
String configFilepath = System.getProperty("user.dir") + "/path/to/configuration/file";

/* setting some default params */
public static Map<String, Object> setDefaultParams() {
Map<String, Object> defaultParams = new HashMap<>();
defaultParams.put("key", "value");
defaultParams.put("key", "value");
defaultParams.put("key", "value");
return defaultParams;
}

@BeforeTest
public void initialiseDriver() {
//Code to initialise driver
}

@BeforeTest
public void signUp() {
//Code to signup
}

@BeforeMethod
public void startScreeRescording() {
//code to record screen

}

@AfterMethod
public void stopScreenRecording() {
//code to stop recording
}

@AfterTest
public void tearDown() {
//code to close all the resources drivers,properties file, etc
}
}

Every test class extends to Base class, doing so every test class has access to non-private variables such as user email and user phone number. Some tests need to have default params, these common default params we set inside a method setDefaultParams(). By doing so we don't need to set variables and default params again and again in the test classes. Also whenever we run a test we need to do sign up and start recording the screen, these were provided as TestNg BeforeTest and AfterTest methods, on running the test method in the test class TestNg will look for BeforeTest, BeforeMethod in the parent class, thus making sure that signup happens before every test and screen recording starts before every test method. We make sure that screen recording is stopped and the driver is quit and other resources are closed in AfterMethod and AfterTest.

Managing WebDrivers

Managing multiple mobile applications and different type of drivers when you have all, in one framework becomes a challenging task. In a framework, a driver can be initialised in different ways, to achieve this we can use an interface. Interfaces specify what a class must do and not how. It is the blueprint of the class.

Let’s see what a driver interface looks like:

public interface IDriver<T> {

T getDriver(String browserName);

T getDriver(BaseConfig baseConfig);

T getDriver(String appName, BaseConfig baseConfig);
}

Implementing the above interface in the driver class

public class CreateAndroidDriver implements IDriver<AndroidDriver>{@Override
public AndroidDriver getDriver(String browserName) {
//provide implementation if using a browser(chrome,firefox etc.)
}
@Override
public AppiumDriver getDriver(BaseConfig baseConfig){
//provide implementation using a BaseConfig
}
@Override
public AppiumDriver getDriver(String appName, BaseConfig baseConfig){
// provide implementation if there are multiple apps and baseConfig
}
}

With inheritance, we will be able to override the methods of the IDriver<T> interface so that the meaningful implementation of the interface methods can be designed in the derived class. We can similar implementations for other dirvers like IOSDriver, WebDriver etc.

Managing Devices

We might need to work with multiple android or iPhone devices while running automation tests. With inheritance, we will be able to manage these devices easily. The following interface shows what can we do with devices.

public interface IDevice {
Device getDevice(String deviceUdid);

List<Device> getDevices();
}

Implementing the above interface:

public class AndroidDeviceManager implements IDevice{@Override
public List<Device> getDevices() {
//Code showing how to get list of all android devices
}
@Override
public Device getDevice(String deviceUdid){
//Code to get a connected android device by Uuid
}

Here AndroidDeviceManger class will provide an implementation of the interface methods defining how we can get an android device by UUID or how can we get all the android devices.

Similarly, we can use an IOSDeviceManager class in the case of iPhones with its own implementation of how to get an iPhone device by UUID or how to get all the connected iPhones

Querying Databases

There are two types of queries that we normally do while automation testing viz. select query and update query, while database servers can be MySQL, Microsoft SQL Server, Oracle, Postgres, Mongo, etc. We can have an interface having the type of queries that we can do and database classes that have the implementation of the interface methods. We can have an interface as follows

public interface IDBManager{List<LinkedHashMap<String, String>> doSelect(
String dbHost,
int port,
String dbName,
boolean isSslEnabled,
String userName,
String password,
String query)
throws SQLException;
int executeUpdate(
String dbHost,
int port,
String dbName,
boolean isSslEnabled,
String userName,
String password,
String query)
throws SQLException;
}

We can have DBManger class that will tell how to connect a database server and perform a query. For Eg:

public class DBManager implements IDBManager{List<LinkedHashMap<String, String>> doSelect(
String dbHost,
int port,
String dbName,
boolean isSslEnabled,
String userName,
String password,
String query)
throws SQLException{
//Code for making connection to mysql server and executing query
}
int executeUpdate(
String dbHost,
int port,
String dbName,
boolean isSslEnabled,
String userName,
String password,
String query)
throws SQLException{//Code for making connection to mysql server and executing query
}
}

Above are some ways where I have used inheritance. There can be n number of ways of using inheritance. Comment if you are using inheritance in some other ways.

--

--

Deepak Attri
CodeX
Writer for

Quality Assurance Engineer | JAVA | API Testing | Mobile Testing | Web Testing | Performance Testing