Test Automation Framework with Layered Architecture
Writing automation scripts is easier than creating a highly maintainable automation framework with any tool. Also, it is quite challenging in the software development life cycle, and if the team size is bigger then the challenge becomes more challenging to create a stable, scalable, and highly maintainable framework.
Having no architectural diagrams at the beginning will lead to circular dependencies and tightly coupled code, which are both bad practices, and make the framework difficult to understand and maintain for newcomers.
Hence, it is essential to design a framework architecture at the beginning of test automation to grasp how to write tests effectively, build maintainable, scalable frameworks, and avoid circular dependencies.
In addition, we do not want to end up with an automation framework that includes a lot of capabilities, but isn’t relevant to test automation, nor do we want to miss any features that are critical to test automation.
In this blog, I’ll share my experiences with Layered Architecture and how I’ve used it in test automation to overcome the challenges mentioned above.
About Layered Architecture
The most common architectural framework in software development is a layered architecture, also known as an n-tier architecture. There are specific roles and responsibilities for each layer of the layered architecture and these layers are logical separations of components or code:
In this Layered architecture, the upper layer only talks to the layer below it, and you are not allowed to jump between layers. Depending on the requirement, you can also have multiple layers in between.
Advantages of the Layered Architecture
- Easy to insert new layer without impacting the existing layers
- Easier Maintenance
- Easy to implement Security layer
- Ease of Testability
Disadvantage of the Layered Architecture
- Performance — Because of multiple layers , request from top layers have to go through multiple layers
Let’s understand how we can apply this layered architecture in a test automation framework.
Test Automation Framework with Layered Architecture Approach
I am going to use the following tech stacks for example to illustrate why layered architecture is important in test automation:
Programming Language — Java
Testing Tool — Selenium WebDriver
Testing Framework — TestNG
Design Pattern — Page Object Model
Without layered architecture
Test Class :
TestBase Class:
It seems to me that you are going to have trouble when you create a WebDriver instance in your TestClass or TestBase class and then use the reference of the WebDriver instance in your page layer as mentioned in the above example.
I’m sure you’re wondering now how this can be wrong. I’ll explain what I mean.
With this approach, the test class is dependent on DriverManager, which is a helper class, as well as page classes. Page classes are used to implement the business logic for performing actions on the UI using Selenium APIs. Again, they are dependent on Helper Classes.
In this case, you’re going to have a circular dependency issue, which will complicate managing, reading, and scaling your automation framework.
Imagine, for instance, that Selenium has undergone a couple of changes in its latest version, which also affects the wrapper methods you have written for your test automation framework. The Test class and Page class are tightly coupled with the Selenium wrapper methods, so both classes need to be updated. Having a poor framework architecture will also make understanding which areas have been impacted by recent changes more difficult. Additionally, giving WebDriver access to TestClass is not recommended.
Doesn’t it seem tedious to update both Test and Page classes for a change in components classes?
Now with a layered architecture, Let’s see how we can improve the framework architecture and have loosely coupled layers.
With layered architecture
In terms of layered architecture , automation framework layers will be separated as follows:
- Presentation layer — The presentation layer is a top-level layer in a layered architecture. so in test automation, the Test layer will be our presentation layer which will act as an interface to users.
- Business Layer — We put our domain logic at the business layer and it also acts as a bridge between Presentation and Persistence Layers.
So, In test automation, we write business logic in Page Classes, hence page objects will come under this Business Layer. - Persistence Layer — The persistence layer is responsible for storing data and files, but in test automation, we can use this layer as a Helper layer which will have various component classes like Selenium helper classes, Data helper, API Helper, Logger, etc.
Furthermore, we also need a layer to keep our test data. Let’s call it the data layer, which will interact directly with the Test layer. To perform a business action, the Test Layer passes the required data to the Page Layer, which in turn interacts with the Persistence Layer for a web-based action.
Note — There should be no code added to the test data layer because it is intended to provide test data to the Test layer only.
This approach would result in the following folder structure:
According to this approach, lower layers only respond to the top layer, so component layers shouldn’t be accessed directly from the test layer. The test layer calls the business logic from the implementation layer, which then interacts with the component layer.
Now Let see the code implementation of layered architecture with same tech stack.
Test Layer — This layer is specific for test framework related like TestNG, Nunit , xUnit and Cucumber etc.
In this layer, we will only call the business logic implemented in the implementation layer as mentioned in the above example.
TestBase Class:
Implementation Layer:
App Class:
LoginPage Class:
As you can see, the Page class is using helper methods like WebTextHelper, WebElementFinder, etc.
This way, component layer dependencies are not present in Test Layer, and only the Implementation layer and components are interacting with each other and replying to Test Layer.
Advantages of using layered architecture in test automation
- Any Change in Components layer have no impact on Test Layer
- Any component can be easily replaced
- Easy to maintain
- Easy to understand
- Multiple test layers can be created for different projects with ease. It is possible to use both Cucumber and TestNG tests in the same test framework without any problems, for instance.
- Component layer restrictions can be enforced using this approach, which allows you to move component layers at different locations where you can consume them as jar dependencies in Java or NuGet packages in C#.
Conclusion
In your test automation project, you can apply this layered architecture, and you can see the benefits during the process.
However, as I mentioned above, the components layer should not be accessed directly in the Test layer as per this approach, so this should be enforced as a practice in your team since it is impossible to prevent users from accessing component layers in the Test layer.
I also suggest not using the same logic with every test automation tool. For example, Cypress and Playwright with Typescript/JavaScript tools provide global variables to perform action on UI and also tool’s architecture make them different from Selenium.
Note — Please avoid writing unnecessary wrapper methods for the Cypress or Playwright tools and do not try to implement the same Selenium framework structure logic with these tools.
Therefore, you need to understand the tool first before you move on to the architectural diagram for test automation, and remember there is no one right approach to the automated framework design problem. Therefore, if the approach doesn’t fit your test automation tool, you can consider another.
I hope this article should give you a better understanding of how to design test automation frameworks and why it’s important not to have tight couplings or circular dependency between your code.
Thanks for Reading!!!