# Understanding Object-Oriented Programming in the Context of Automation QA

📱 Are you starting your journey in test automation? You might have written a few scripts and noticed something? They can get messy. Fast. As an application grows, test scripts that once worked perfectly become tangled as spaghetti, hard to read, and a nightmare to update. Believe me, we all have been there in some point. There is a rework in the application and suddenly, all test are failing.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754457785529/69a563dc-4648-4fa7-9415-5566f9f8cdb8.jpeg align="center")

What if there was a way to build your test suites like engineers build fantastic, scalable applications?

There are a few, and one of them is called **Object-Oriented Programming (OOP)**. This article is your guide to understanding OOP principles and applying them in TypeScript to write clean, powerful, and maintainable automated tests.

---

### 🤔 What is Object-Oriented Programming?

At its core, OOP is a way of thinking about and organizing code. Instead of writing long, procedural scripts, you structure your code around **objects**.

Think of it like using LEGOs. You have different types of bricks (blueprints, or [**Classes**](https://idavidov.eu/stop-writing-brittle-tests-your-blueprint-for-a-scalable-typescript-pom) in OOP) that you can use to build specific things (structures, or [**Objects**](https://idavidov.eu/how-to-use-arrays-and-objects-in-typescript-for-powerful-qa-automation-scripts)). Each object has its own properties (like color) and things it can do (like connect to other bricks).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754457801910/06f68ce8-d25d-4eaa-8f78-11ffbb0c0e70.jpeg align="center")

For QA automation, this means we stop thinking in terms of individual lines of code and start thinking in terms of application components, like a `LoginPage` object or a `User` object. This shift makes our test automation **scalable**, **reusable**, and much easier to **maintain**.

---

### 🏛️ The Four Pillars of OOP in Test Automation

**OOP** stands on four main pillars. Let's break down each one with a practical example from the world of QA.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754457744179/5c55d3a1-7b1b-4d4c-850d-c28e53dcda23.jpeg align="center")

#### 1\. Encapsulation (📦): Bundling Data and Actions

**Encapsulation** means bundling the data (variables) and the methods (functions) that operate on that data into a single unit, or "object." It also means hiding the object's internal state from the outside.

**In QA Terms:** Imagine a login page. It has UI elements (username field, password field, login button) and actions you can perform (entering text, clicking the button). Encapsulation means we create a `LoginPage` class that bundles the locators for those elements and the methods to interact with them.

If a locator changes, you only have to update it in **one place**: the `LoginPage` class. Your actual test script doesn't need to change at all!

```typescript
class LoginPage {
  // Data (locators) are kept private
  private readonly usernameInput = '#username';
  private readonly passwordInput = '#password';
  private readonly loginButton = '#login-btn';

  // Methods (actions) are public
  public enterUsername(username: string) {
    // Code to find element and type text
    console.log(`Typing '${username}' into ${this.usernameInput}`);
  }

  public enterPassword(password: string) {
    // Code to find element and type text
    console.log(`Typing password into ${this.passwordInput}`);
  }

  public clickLogin() {
    // Code to find element and click
    console.log(`Clicking ${this.loginButton}`);
  }
}
```

#### 2\. Inheritance (🧬): Reusing Common Code

**Inheritance** allows you to create a new class (a "child") that inherits properties and methods from an existing class (a "parent"). This promotes code reuse.

**In QA Terms:** Most pages in an application share common elements, like a header, a footer, or a navigation menu. Instead of re-writing the code for these elements on every single page object, we can create a `BasePage` and have other pages inherit from it.

```typescript
// Parent Class
class BasePage {
  public getFooterText(): string {
    // Code to find footer element and get its text
    return "Copyright 2025 Intelligent Quality";
  }
}

// Child Classes
class HomePage extends BasePage {
  public getWelcomeMessage(): string {
    return "Welcome to our blog!";
  }
}

class ProfilePage extends BasePage {
  public getUsername(): string {
    return "Ivan Davidov";
  }
}

// Now, both HomePage and ProfilePage have access to getFooterText()!
const homePage = new HomePage();
console.log(homePage.getFooterText()); // Outputs: "Copyright 2025 Intelligent Quality"
const profilePage = new HomePage();
console.log(profilePage.getFooterText()); // Outputs: "Copyright 2025 Intelligent Quality"
```

#### 3\. Abstraction (☁️): Hiding Complexity

**Abstraction** means hiding the complex implementation details and showing only the essential features to the user. It simplifies a complex system by modeling classes appropriate to the problem.

**In QA Terms:** Your test script should be simple and readable. It should describe *what* the test is doing, not *how* it's doing it.

We can create a high-level method like `login()` inside our `LoginPage` class. This one method will handle all the smaller steps: finding the username field, typing, finding the password field, typing, and clicking the login button. The test itself just needs to make a single, clear call.

```typescript
// Let's add a high-level method to our LoginPage class
class LoginPage {
  // ... previous private locators

  public enterUsername(username: string) { /* ... */ }
  public enterPassword(password: string) { /* ... */ }
  public clickLogin() { /* ... */ }

  // Abstraction in action!
  public login(username: string, password: string) {
    this.enterUsername(username);
    this.enterPassword(password);
    this.clickLogin();
  }
}

// Now, look how clean the test file is:
// In login.test.ts
const loginPage = new LoginPage();
loginPage.login("admin", "password"); // One line, super readable!
```

#### 4\. Polymorphism (🎭): One Action, Many Forms

**Polymorphism** allows a single action or method to be performed in different ways, depending on the object it is being performed on. The name literally means "many forms."

**In QA Terms:** This is a more advanced concept, but imagine you have different types of users in your system (e.g., `GuestUser`, `PaidUser`) and you want to run a test that searches for an item. The search results page might look different for each user type.

With polymorphism, you could have a single `search()` function that correctly handles the results verification for whatever user type is currently logged in. To achieve this, we’ll define a common "contract" with an [interface](https://idavidov.eu/a-practical-guide-to-typescript-custom-types-for-qa-automation).

```typescript
interface User {
  verifySearchResults(): void;
}

class GuestUser implements User {
  public verifySearchResults() {
    console.log("Verifying search results with 'Sign Up' banner...");
  }
}

class PaidUser implements User {
  public verifySearchResults() {
    console.log("Verifying search results with NO banner...");
  }
}

// Your test can work with any user that fits the IUser interface
function runSearchTest(user: User) {
  // ... code to perform search ...
  user.verifySearchResults(); // This will call the correct method
}
```

---

### 🧩 Putting It All Together: The Page Object Model (POM)

If you've heard of a design pattern called the [**Page Object Model (POM)**](https://idavidov.eu/stop-writing-brittle-tests-your-blueprint-for-a-scalable-typescript-pom), you've seen a perfect application of OOP principles.

* **Encapsulation:** POM uses classes to represent pages, bundling locators and user interactions.
    
* **Abstraction:** Tests become clean, calling high-level methods like `loginPage.login()` instead of low-level browser commands.
    
* **Inheritance:** A `BasePage` is often used to handle common headers, footers, and other shared functionalities.
    
* **Polymorphism:** A single test function, like `verifySearchResults()`, can be written to work with different user types (e.g., `GuestUser`, `PaidUser`), where each type handles the verification in its own specific way.
    

POM is the industry-standard result of applying OOP to test automation, leading to a robust and maintainable framework.

---

### ✅❌ Pros and Cons of OOP in Automation

OOP is powerful, but it's important to have a balanced view.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1754457823071/c6b87651-c913-46e1-87dc-9d7bdb2487b1.jpeg align="center")

#### **Pros**

* **✨ Maintainability:** Code is far easier to update. Change a button's ID? You only edit one line in its page class.
    
* **♻️ Reusability:** Share code across tests with inheritance and utility classes, saving time and reducing duplication.
    
* **📈 Scalability:** Your test suite can grow to hundreds or thousands of tests without becoming a tangled mess.
    
* **👓 Readability:** Test cases read like a list of user actions, making them easy for anyone (even non-technical stakeholders) to understand.
    

#### **Cons**

* **⏳ Initial Overhead:** Setting up a proper OOP framework takes more time upfront than writing a simple script.
    
* **🧠 Learning Curve:** It requires a solid understanding of these concepts, which can be a hurdle for those new to programming.
    
* **🚨 Risk of Over-Engineering:** For a tiny project, a full-blown OOP framework can be overkill. It's also possible to create anti-patterns like **"God Objects"**—a single, massive page object that tries to do everything, defeating the purpose of clean separation.
    

---

### 🚀 Your Path Forward

For manual testers and junior QAs, learning Object-Oriented Programming is not just an academic exercise. Instead this is a fundamental step toward becoming a highly effective automation engineer. It's the difference between writing disposable scripts and building a professional-grade, long-lasting test automation framework.

> **🙏🏻 Thank you for reading!** Building robust, scalable automation frameworks is a journey best taken together. If you found this article helpful, consider joining a growing community of QA professionals 🚀 who are passionate about mastering modern testing.

> [Join the community and get the latest articles and tips by signing up for the newsletter.](https://idavidov.eu/newsletter)
