Polymorphism is the third subject we’ll discuss in our OOP principles in test automation series. Before we start, make sure you are already familiar with encapsulation and inheritance 🔎 Let’s dive in:
Table of Contents
- Chapter 1 – Encapsulation
- Chapter 2 – Inheritance
- You’re here → Chapter 3– Polymorphism
- Chapter 4 – Abstraction
Polymorphism in OOP
Polymorphism definition
Polymorphism comes from the Greek words “poly” (meaning many) and “morphe” (which means form). So, in short, polymorphism refers to something that can take multiple forms. In programming, this means that we can perform an action in multiple ways. In C#, there are two types of polymorphism:
- Compile-time polymorphism (aka overloading).
- Run-time polymorphism (aka overriding).
Overloading
Overloading allows us to have multiple definitions for the same method. The methods must have different signatures, which means they take different parameters and/or they have different return types.
Let’s say we need to calculate the sum of 2 or 3 numbers. We could use two separate methods, e.g. CalculateSumOfTwo() and CalculateSumOfThree(), or we can have different overloads of the same method:
public int CalculateSum(int a, int b) => a + b; public double CalculateSum(double a, double b) => a + b; public int CalculateSum(int a, int b, int c) => a + b + c;
As you can see, we can even use different overloads for different data types, i.e. int and double. Now, when we want to calculate the sum, we don’t have to worry about which method to use based on the parameters we need to pass ✅
SumCalculator s = new SumCalculator(); Console.WriteLine(s.CalculateSum(4,5)); Console.WriteLine(s.CalculateSum(4.5, 4.5)); Console.WriteLine(s.CalculateSum(4, 5, 6));
The compiler will know each time what overload to use, and the output will look like this:
Visual Studio will also show you when you have multiple overloads for a method:
As you start typing the parameters, you can also cycle through the overloads to see what you can pass (by pressing the arrows), without having to navigate to the method implementation:
If you use NUnit in your test automation framework, you might have seen this type of polymorphism in the Assert class, for example:
You can use the IsTrue() method like this:
Assert.IsTrue(adminPage.IsUserLoggedIn("Paul"));
Or like this (here we display a message when the assertion fails):
Assert.IsTrue(adminPage.IsUserLoggedIn("Paul"), "Login failed");
Overriding
The second type of polymorphism is overriding. It allows us to rewrite a method (or properties, events, indexers) from the base class in a derived class, providing a different implementation.
In order to override a method, the method in the base class must be declared as virtual. You cannot override non-virtual methods. In the derived classes, the methods must be declared with the override modifier.
Let’s go back to our Animal class for illustration- As before, we will use a base class, Animal, where we define the methods that should apply to all types of animals 🐵 Except for this time, we want the Eating() method to have specific implementations where needed. So our class should look like this:
class Animal { public virtual void Eat() => Console.WriteLine("Eating"); public void Sleep() => Console.WriteLine("Sleeping"); }
In our derived classes, we can now override the method. So let’s say we have these two classes:
class Dog : Animal { public void Bark() => Console.WriteLine("Barking"); public override void Eat() => Console.WriteLine("Eating chicken"); } class Cat : Animal { public void Meow() => Console.WriteLine("Meowing"); }
Now we can create new instances of our Cat and Dog classes, and use the methods like this:
Dog dog = new Dog(); Cat cat = new Cat(); dog.Bark(); dog.Eat(); dog.Sleep(); cat.Meow(); cat.Eat(); cat.Sleep();
In the Cat class, we did not override the Eat() method, so the implementation used will be the one from the base Animal class. This will be the output:
How to use polymorphism in test automation
Now that we understand how polymorphism works in general, let’s see how we can apply it in test automation 💡
Polymorphism overload example in test automation
I will use the OrangeHRM website as an example. For the login page, you might want to do a test for a valid login, as well as a test that verifies that the correct message is displayed when the password is not provided.
This means that in the first test, we provide the username and password, click the Login button, and verify that the user is logged in. In the second test, we only provide the username, click the Login button, and verify the error message. We can have two separate methods, or we can have two overloads of the Login() method, like this:
public BasePage Login(string username, string password) { NameInput.SendKeys(username); PasswordInput.SendKeys(password); LoginButton.Click(); return new BasePage(driver); } public LoginPage Login(string username) { NameInput.SendKeys(username); LoginButton.Click(); Console.WriteLine(ErrorMessage.Text); return this; }
The two overloads have different signatures – one has 2 string parameters, and it returns a BasePage, while the second one has only one parameter, and returns a LoginPage. Depending on what we need to test, we simply pass the required parameter(s) and let the tests do their job.
[Test] public void ValidLoginTest() { LoginPage loginPage = new LoginPage(driver); loginPage.OpenLoginPage(); loginPage.Login("admin", "admin123"); AdminPage adminPage = new AdminPage(driver); Assert.IsTrue(adminPage.IsUserLoggedIn("Paul"), "Login failed"); }
[Test] public void InvalidLoginTest() { LoginPage loginPage = new LoginPage(driver); loginPage.OpenLoginPage(); loginPage.Login("admin"); Assert.IsTrue(loginPage.IsMessageDisplayed("Password cannot be empty")); }
Polymorphism override example in test automation
In test automation, you might have a common behavior in most pages, i.e. a method in the base page class, and a page where this behavior differs. If this is the case, you can override the method in the derived class.
I am not sure I had to use override in test automation until now, but for the sake of the example, I will show how it would look like using the same project as earlier. I will add a method that verifies the title of the page in the BasePage class:
public virtual bool IsTitleCorrect() { return driver.Title.Equals(title); }
Just like before, I declared it using the virtual keyword, to allow it to be overridden in the derived page classes. Next, I’ll create a new page class, for the Admin users. Here, I add the same method, but with a specific title check:
class AdminPage : BasePage { public AdminPage(IWebDriver driver) : base(driver) { } public override bool IsTitleCorrect() { return driver.Title.Equals("OrangeHRM"); } }
Conclusions
Polymorphism can be a bit more complex than the previously discussed principles. It’s also possible that you won’t really need to use it in your automation framework. However, it doesn’t hurts to know and understand it. This way when you do need it, you already know how to implement it and how it can help your project 💫