Writing automation tests requires the equal mindset as writing code for a software, in most cases this rule is forgotten by many people, and they run in technical debt in the future. When writing the automation tests the main focus is to cover the functionalities that are currently developed, and the tests are designed according to the present requirements. I cannot agree more that this is working perfectly fine at the beginning 🙂 The problem occurs when you have to maintain the automation solution, avoid developing flaky automation tests, producing report for he automation tests, etc. All of this could be avoided if we pay attention at the beginning, and we take care of the architecture of the automation tests.
How to proceed
In this post I’m going to talk about the clean architecture when using Selenium + Cucumber + Java using Behavior Driven Development (BDD) methodology. The reason I choose the BDD methodology is because it allows more freedom of reusing steps in existing feature, and allowing you to speed up the automation tests development. Another benefit from this approach is that when you have to fix one obsolete step, the same fix is going to be applied in other tests for the same feature.
This is how I prefer to have the core architecture separated for my automation tests:
In the configuration package you should add the class which is going to hold the responsibility for reading the configurable property parameters.
In the dao package you should add the dao implementation depending on the database which is used with your web application. The dao class is very useful for setting / reseting the scenario data, and returning query data for validation.
In the core/driver package I’m using class for handling the state of the driver, and additional class for allocating the web elements. Lets call the class for handling the state of the driver DriverManager, the purpose of this class should be to initialize the web driver according the predefined driver configuration (chrome, firefox, etc), destroy the driver instance, and return the initialized driver instance. In order for this to work the WebDriver class should be declared as private static variable, the reason for this is that Cucumber does not allow to inherit the class that handles the Before and After annotations.
I’m going to call the class for allocating the elements ElementAllocator, the responsibility for this class is to interact with the UI elements in the web application. The reason for this class is to separate the WebDriver from the page classes, and use the ElementAllocator for interacting with the UI elements.
In the page package I’m using BasePage class which is going to implement the ElementAllocator class, and later used in the page implementations for interacting with the web elements. This separation is going to allow you to focus only on implementing the business logic for interacting with the UI elements of the page, and don’t think about the driver.
In the scenario package I’m using the ScenarioCycle class which is handling the Before and After cucumber methods. In these two methods it is handled the state of the driver, by implementing the DriverManager class in the ScenarioCycle class. This separation is going to allow you to initialize the driver when the scenario is started, and destroy the driver when the scenario is finished.
You should create pages packages which is going to contain the implementation of the page classes. This means that the page class is going to extend the BasePage class mentioned previously, and this should allow you access to the ElementAllocator for interacting the UI elements.
You should create steps package which is going to contain the step definitions of the steps defined in the feature file. When creating the steps in the feature file, make sure each step contains the minimum needed logic. This approach is going to allow you to reuse the same step in multiple scenarios. If you use one complex step in your scenario, then this is going to lead to copy the 90% of the step for different scenario which is going to require small change in the complex step. This is bad habit because you are going to end up with duplicate code, which is really hard for maintaining in the future, and lead to technical debt.
By reading this post, I believe it was helpful, and you have gained general information how to separate the core logic in your automation test scenarios, and extend the productivity. When separating the logic in your automation scenarios solution, it is going to open the doors for further extension of the core classes, which I’m going to talk about in the next posts. If you liked this post don’t forget to share it with your friends 🙂