top of page

Welcome
to NumpyNinja Blogs

NumpyNinja: Blogs. Demystifying Tech,

One Blog at a Time.
Millions of views. 

Understanding SOLID Principles in Test Automation Framework Design

Tired of hearing that your framework should be robust, scalable, and easy to maintain for continuous testing? Are you feeling the same? These words sound so familiar and common, but do we really understand what they mean and how they should be applied? Let’s try to understand what a framework is, why it is used, and how it can be made better by using SOLID principles.


What is Framework?

In general, Framework means a supporting structure around which something can be built. In test automation, we are creating a structure that supports and make our testing jobs easier and more efficient.

 

Why we need Framework?

Testing activity is not dependent on framework; it can be carried out without a framework. Let’s try to understand with an example: testing a login page of salesforce portal without using a framework structure.

ree

Test Case ID

Description

Steps

Expected Result

TC001

Login with valid username and password

1. Open login page 2. Enter valid username 3. Enter valid password 4. Click "Log In"

User is logged in successfully

TC002

Login with invalid username

Enter invalid username and valid password

Error message- "Invalid username or password"

TC003

Login with blank fields

Leave both fields empty and click "Log In"

Error message prompting to fill required fields

TC004

Check "Remember Me" functionality

Select checkbox and login

Username is remembered on next visit

TC005

Forgot Password flow

Click "Forgot Your Password?"

Redirects to password recovery page

 

 Requirement Change: Replace “Username” with “Email”

Now imagine that product team says, “We’re switching from username to email login.” Without a framework, here’s what happens:


Without Framework

If you’ve written scripts, you now do below changes:

  •   You manually update every test case to replace “username” with “email.”

  •   Change every locator tied to the username field.

  •   Update validation logic.

  •   Modify test data.

  •   Re-run all tests manually to verify changes.



With Framework

  •   Changes made in a single class (Login Page Class) handle all login page web elements.

  •   Update the test data in excel from username to email ID rather than touching the code


Results – Changes are minimal, don’t need to redesign the structure or code.


So yes, it is better approach to do testing with a framework, instead of just writing the code and testing it. To improve the framework and make it more efficient, we can use SOLID principles.

 

What are SOLID principles?                  

These principles come from object-oriented programming and help you write better, more organized code.


ree

  •   Single Responsibility Principle (SRP)

  •   Open/Closed Principle (OCP)

  •   Liskov Substitution Principle (LSP)

  •   Interface Segregation Principle (ISP)

  •   Dependency Inversion Principle (DIP)


I know this term scares more than robust, scalable. Same here!! Let’s make it easy to understand and implement.

Single Responsibility Principle (SRP) – It states do one job and make it perfect!!

Assume you are participating in running race, and trying to dance, sing simultaneously. Sounds crazy, right? The same applies to our framework.

To make our automation framework easy to maintain and adaptable we should design a class that focus on one core functionality.

For example, consider a WebDriver and browser setup which is used in multiple class, rather than creating in each class, it is better to handle in one place.

ree

Open/Closed Principle (OCP) – It states, open to add new things and closed to do modifications.

Assume you are drawing the different shapes, would like to add new shapes or colors to your painting book. Or maybe you want to make the changes to your previous drawing.

Sounds familiar? In automation frameworks, the same idea applies: your code should be open for extension (you can add new features in existing code) but closed for modification (you don’t need to alter the existing code to include new feature).

For example, let’s assume the creating the Shapes interface, you are calculating the area of rectangle and in next sprint if you need to calculate the circle, you can add another class implementing the Shapes interface rather than altering existing codes. This approach helps to maintain the framework for continuous testing.

ree

ree


Liskov Substitution Principle (LSP) – It states that in an object-oriented program, if we substitute a superclass object reference with an object of any of its subclasses, the program should not break.

Let’s try to convert this statement into simpler words: every subclass or derived class should be substitutable for its parent or superclass without altering the expected behavior.

Assume your parent is a math teacher and you are very interested in physics. You can inherit your parent's math knowledge and use it to solve physics problems. But if your sibling is interested in dance, they can’t inherit your parent's math knowledge for their profession. So here, it's violating the LSP principle.

Now let’s take an example from an automation framework. Think you have a Parent class which helps you to sign in to the application, and you are using the parent object for all subclasses to sign in and perform certain actions. But the same Parent class needs to be overridden to test the login functionalities, as we are testing with multiple test data like valid and invalid combinations. The key is to apply inheritance where it makes sense, without breaking the original logic of the parent class.

ree


By following the Liskov Substitution Principle, we ensure that our test automation framework remains flexible, scalable, and maintainable.


Interface Segregation Principle (ISP) states that clients should not be forced to depend on methods they don’t use.

In simpler terms, an interface should only have methods that are relevant to the class implementing it. If an interface becomes too large, it should be broken down into smaller, more specific ones so that classes can implement only what they really need.

Think of it like getting a license. If you have a car license, can you use it to fly a plane or drive a boat? Of course not, each vehicle requires its own license. The same logic applies to automation testing; you shouldn’t create one big interface that tries to handle everything. Instead, build smaller, purpose-specific interfaces that serve distinct functionalities. This approach makes your framework more flexible, scalable, and easier to maintain.


You have a DriverFactory that is responsible only for browser setup and teardown, and a separate LoginPage class that handles login functionality. You shouldn’t force DriverFactory to implement login methods because that violates ISP. This helps maintain cleaner code.


ree

 Dependency Inversion Principle (DIP) states High-level objects should not depend on low-level objects. Both high and low-level objects should depend on abstractions.

Think of it like glasses. Power glasses depend on a person’s specific eye power, while sunglasses or goggles can be used by almost anyone without that dependency. The frame (low-level part) depends on the shape and size of the lens, while the lens (high-level part) performs the main task of correcting vision or protecting the eyes. In the case of power glasses, both frame and lens depend on the patient’s eye condition (without DIP concept), but with sunglasses or goggles, the dependency isn’t tied to an individual’s vision(with DIP concept).

Let’s apply the same principle in the automation framework for CustomListener class which implements ITestListener.



 

ree

ree

ITestListener (from TestNG) → Abstraction

CustomListener → Low-level implementation of that abstraction

Testng.xml file -> High-Level which depends on abstraction

In this setup, the high-level part (testng.xml) doesn’t directly depend on the low-level class (CustomListener). Instead, both the high-level (testng.xml) and low-level (CustomListener) depend on the abstraction (ITestListener). This aligns perfectly with the Dependency Inversion Principle (DIP) and makes your code more flexible to future changes or modifications.

 

To conclude, by applying the SOLID principles to the framework, you can make your framework more robust, scalable and easy to maintain for continuous testing!!

 

Happy Testing!!

 
 

+1 (302) 200-8320

NumPy_Ninja_Logo (1).png

Numpy Ninja Inc. 8 The Grn Ste A Dover, DE 19901

© Copyright 2025 by Numpy Ninja Inc.

  • Twitter
  • LinkedIn
bottom of page