Practical Examples

Introduction

Polymorphism is one of the most powerful features of Object-Oriented Programming (OOP). It allows different objects to respond differently to the same method call. In JavaScript, polymorphism is primarily achieved through inheritance and method overriding.

Instead of writing separate code for every object type, developers can write generic code that works with multiple objects. Each object executes its own implementation of the method, making applications flexible, scalable, and easier to maintain.

In real-world Node.js applications, polymorphism is used in payment systems, notification services, employee management systems, file processing, API integrations, and many other scenarios.

For automation engineers, polymorphism is widely used in Playwright, Selenium, and Cypress frameworks where different page objects, browser classes, API clients, and reporting modules implement common methods differently.

In this tutorial, you’ll explore practical examples of polymorphism in Node.js.


Example 1: Animal Sounds

Different animals produce different sounds.

class Animal {

    sound() {

        console.log(
            "Animal makes a sound."
        );

    }

}

class Dog extends Animal {

    sound() {

        console.log(
            "Dog barks."
        );

    }

}

class Cat extends Animal {

    sound() {

        console.log(
            "Cat meows."
        );

    }

}

const animals = [

    new Dog(),

    new Cat()

];

animals.forEach(animal => {

    animal.sound();

});

Sample Output

Dog barks.
Cat meows.

Example 2: Employee Management System

Different employee roles perform different work.

class Employee {

    work() {

        console.log(
            "Employee is working."
        );

    }

}

class Developer extends Employee {

    work() {

        console.log(
            "Writing code."
        );

    }

}

class Tester extends Employee {

    work() {

        console.log(
            "Testing application."
        );

    }

}

const employees = [

    new Developer(),

    new Tester()

];

employees.forEach(employee => {

    employee.work();

});

Sample Output

Writing code.
Testing application.

Example 3: Payment System

Different payment methods process payments differently.

class Payment {

    pay() {

        console.log(
            "Processing payment."
        );

    }

}

class CreditCard extends Payment {

    pay() {

        console.log(
            "Payment using Credit Card."
        );

    }

}

class UPI extends Payment {

    pay() {

        console.log(
            "Payment using UPI."
        );

    }

}

class Cash extends Payment {

    pay() {

        console.log(
            "Payment using Cash."
        );

    }

}

const payments = [

    new CreditCard(),

    new UPI(),

    new Cash()

];

payments.forEach(payment => {

    payment.pay();

});

Sample Output

Payment using Credit Card.
Payment using UPI.
Payment using Cash.

Example 4: Notification System

Different notification services.

class Notification {

    send() {

        console.log(
            "Sending notification."
        );

    }

}

class EmailNotification extends Notification {

    send() {

        console.log(
            "Sending Email."
        );

    }

}

class SmsNotification extends Notification {

    send() {

        console.log(
            "Sending SMS."
        );

    }

}

class PushNotification extends Notification {

    send() {

        console.log(
            "Sending Push Notification."
        );

    }

}

const notifications = [

    new EmailNotification(),

    new SmsNotification(),

    new PushNotification()

];

notifications.forEach(notification => {

    notification.send();

});

Sample Output

Sending Email.
Sending SMS.
Sending Push Notification.

Example 5: File Processing System

Different file types are processed differently.

class FileProcessor {

    process() {

        console.log(
            "Processing file."
        );

    }

}

class CsvProcessor extends FileProcessor {

    process() {

        console.log(
            "Processing CSV file."
        );

    }

}

class JsonProcessor extends FileProcessor {

    process() {

        console.log(
            "Processing JSON file."
        );

    }

}

const files = [

    new CsvProcessor(),

    new JsonProcessor()

];

files.forEach(file => {

    file.process();

});

Sample Output

Processing CSV file.
Processing JSON file.

Example 6: Playwright Automation Example

Different page objects implement login differently.

class BasePage {

    login() {

        console.log(
            "Default login."
        );

    }

}

class AdminPage extends BasePage {

    login() {

        console.log(
            "Admin login."
        );

    }

}

class CustomerPage extends BasePage {

    login() {

        console.log(
            "Customer login."
        );

    }

}

const pages = [

    new AdminPage(),

    new CustomerPage()

];

pages.forEach(page => {

    page.login();

});

Sample Output

Admin login.
Customer login.

Example 7: Selenium Automation Example

Different browsers.

class Browser {

    launch() {

        console.log(
            "Launching browser."
        );

    }

}

class ChromeBrowser extends Browser {

    launch() {

        console.log(
            "Launching Chrome."
        );

    }

}

class FirefoxBrowser extends Browser {

    launch() {

        console.log(
            "Launching Firefox."
        );

    }

}

const browsers = [

    new ChromeBrowser(),

    new FirefoxBrowser()

];

browsers.forEach(browser => {

    browser.launch();

});

Sample Output

Launching Chrome.
Launching Firefox.

Example 8: Cypress Automation Example

Different application pages.

class BaseCommands {

    openPage() {

        console.log(
            "Opening page."
        );

    }

}

class DashboardPage extends BaseCommands {

    openPage() {

        console.log(
            "Opening Dashboard."
        );

    }

}

class SettingsPage extends BaseCommands {

    openPage() {

        console.log(
            "Opening Settings."
        );

    }

}

const pages = [

    new DashboardPage(),

    new SettingsPage()

];

pages.forEach(page => {

    page.openPage();

});

Sample Output

Opening Dashboard.
Opening Settings.

Example 9: API Testing Example

Different API services.

class ApiService {

    sendRequest() {

        console.log(
            "Sending API request."
        );

    }

}

class UserApi extends ApiService {

    sendRequest() {

        console.log(
            "Sending User API request."
        );

    }

}

class ProductApi extends ApiService {

    sendRequest() {

        console.log(
            "Sending Product API request."
        );

    }

}

const apis = [

    new UserApi(),

    new ProductApi()

];

apis.forEach(api => {

    api.sendRequest();

});

Sample Output

Sending User API request.
Sending Product API request.

Example 10: Report Generation System

Different report types.

class Report {

    generate() {

        console.log(
            "Generating report."
        );

    }

}

class PdfReport extends Report {

    generate() {

        console.log(
            "Generating PDF report."
        );

    }

}

class ExcelReport extends Report {

    generate() {

        console.log(
            "Generating Excel report."
        );

    }

}

const reports = [

    new PdfReport(),

    new ExcelReport()

];

reports.forEach(report => {

    report.generate();

});

Sample Output

Generating PDF report.
Generating Excel report.

Common Mistakes

Forgetting to Override the Method

If the child class does not override the parent method, the parent implementation is executed.


Using Different Method Names

Method overriding requires the child class to use the same method name as the parent class.


Forgetting the extends Keyword

Without inheritance, polymorphism cannot be achieved using method overriding.


Best Practices

  • Create generic parent classes.

  • Override methods only when behavior should change.

  • Keep method names consistent across parent and child classes.

  • Use super.methodName() when parent behavior should also be executed.

  • Avoid duplicate code.

  • Keep child classes focused on specialized behavior.

  • Design reusable class hierarchies.


Conclusion

Polymorphism enables different objects to respond differently to the same method call, making applications flexible, scalable, and easier to maintain. In JavaScript, it is commonly implemented using inheritance and method overriding.

For automation engineers, polymorphism is widely used to create reusable Page Object Models (POM), browser classes, API clients, reporting modules, and other framework components where each object provides its own implementation of common methods.

Mastering polymorphism will help you write cleaner, more reusable, and more maintainable Node.js applications.


Frequently Asked Questions (FAQs)

What is polymorphism?

Polymorphism is the ability of different objects to respond differently to the same method call.


How is polymorphism achieved in JavaScript?

It is primarily achieved through inheritance and method overriding.


Why is polymorphism useful?

It improves code reuse, flexibility, maintainability, and scalability.


Can multiple child classes override the same method?

Yes. Each child class can provide its own implementation of the inherited method.


Why is polymorphism important in automation testing?

It allows different page objects, browser classes, API clients, and reusable framework components to implement common methods differently while maintaining a consistent interface.


Key Takeaways

  • Polymorphism means “many forms.”

  • Different objects can execute different implementations of the same method.

  • JavaScript achieves polymorphism through inheritance and method overriding.

  • Polymorphism improves flexibility and code reuse.

  • Parent classes define common behavior, while child classes provide specialized implementations.

  • The same method call can produce different results depending on the object.

  • Polymorphism is widely used in Playwright, Selenium, Cypress, and API testing frameworks.

  • Use super when parent behavior should also be executed.

  • Keep parent classes generic and child classes specialized.

  • Polymorphism is a fundamental principle of Object-Oriented Programming in Node.js.