This is the second part of my OOP principles series in test automation with inheritance. After understanding encapsulation, let’s understand inheritance and why it has a very important role in code reusability 🔁
Table of Contents
- Chapter 1 – Encapsulation
- You’re here → Chapter 2 – Inheritance
- Chapter 3 – Polymorphism
- Chapter 4 – Abstraction
What is inheritance in OOP?
Inheritance definition
Inheritance in OOP is achieved when one object acquires (inherits) the properties and the behaviors of the parent object. This means that the protected and public members of the parent class can be reused in the derived class, without having to define them all over again.
This makes the code easy to be reused, as well as easily maintained ✅ If any changes need to be done, you can do them directly in the parent class.
Inheritance example
The easiest way to understand inheritance is through examples. Let’s assume we have a parent class, called Animal. In this class, we have methods that apply to any kind of animal, for example, Eat() and Sleep():
public void Eat() => Console.WriteLine("Eating"); public void Sleep() => Console.WriteLine("Sleeping");
Next, we can have two separate classes, Cat and Dog, which are derived classes of the Animal class. The syntax for inheritance in C# is a semicolon between the class names. This is how the two classes can look:
class Dog : Animal { public void Bark() => Console.WriteLine("Barking"); }
class Cat : Animal { public void Meow() => Console.WriteLine("Meowing"); }
When we create new instances of the Dog or the Cat classes, we will be able to use the methods inside these classes, as well as the ones from the class they inherit (Animal):
Dog dog = new Dog(); Cat cat = new Cat(); dog.Bark(); dog.Eat(); dog.Sleep(); cat.Meow(); cat.Eat(); cat.Sleep();
The output will look like this:
In addition, if the inherited class has any protected methods, this can be accessed from within the derived classes, without instantiating an object of the parent class:
class Animal { public void Eat() => Console.WriteLine("Eating"); public void Sleep() => Console.WriteLine("Sleeping"); protected void Drink() => Console.WriteLine("Drinking"); } class Cat : Animal { public void DrinkAndEat() { Drink(); Eat(); } }
Inheritance in POM and test automation
In test automation, inheritance can be very useful when we use the page object model, and we want to have a base page. For example, when the application we test has some common elements on all pages: headers, side menus, etc.
Another scenario is where we have the same setup and teardown for all our tests. In this case, it’s useful to have a base test class, which all the other test classes will inherit. Let’s see how this looks with an actual example:
Inheritance example in test automation
Base page
I will return to the same example I used in the previous post, which is the OrangeHRM demo site. After logging in, we can see that many elements are common, whether the logged-in user is an Admin or not, and regardless of the landing page.
Let’s look at the logged user. We can see that the name is always displayed in the corner, and the menu (About, Support, Login) is always available:
If I have separate page objects for, let’s say, the Dashboard and User Management page, there is no use in implementing any methods related to this menu in each page class, because the menu is not specific to that page.
And of course, we don’t want to implement them in each of these pages, because that would mean code duplication and that goes against the most common best practices ❌ So the solution is to implement a base page class, where we can have all the common methods. The other pages will simply inherit it.
public class BasePage { protected readonly IWebDriver driver; private string title; private IWebElement LoggedUser => driver.FindElement(By.Id("welcome")); public BasePage(IWebDriver driver) { this.driver = driver; } public bool IsUserLoggedIn(string username) { return LoggedUser.Text.Contains(username); } }
In the above example, the base page has a method that verifies that the correct user is logged in. Of course, in real life, we will have more complex methods and classes, but this should give a good idea of how inheritance works in test automation.
Moving on, all other page object classes can inherit this class, and we can then use the method in our tests after we instantiate those classes. The derived classes will look like this:
class AdminPage : BasePage { public AdminPage(IWebDriver driver) : base(driver) { } }
Of course, the class will have other specific methods. Now, when we want to verify that the correct user is logged in, it’s enough to create a new instance of the AdminPage:
AdminPage adminPage = new AdminPage(driver); Assert.IsTrue(adminPage.IsUserLoggedIn("Paul"));
We did not create an instance of the BasePage, but the build will still pass, and the test as well, provided that the logged user is called “Paul”.
Base test
As mentioned before, another way to use inheritance in test automation is to have a base test class 🔍 This is where we can store the setup and teardown, which are common to all tests:
class BaseTest { protected IWebDriver driver; [SetUp] public void Setup() { driver = new ChromeDriver(); } [TearDown] public void Teardown() { driver.Quit(); } }
All subsequent test classes will be derived from this one, using the same syntax:
class DashboardTests : BaseTest
Now, when we run any test inside this class, the setup and teardown will run right before the other test steps.
Conclusions
Inheritance plays an important role in OOP, as well as test automation. If you understand it well, it can help you save a lot of time and create a testing framework that is easy to use and maintain ✨