How to Design Test Automation Framework in less than 30 minutes?
In an Agile Software-Development environment, Time is of critical essence.
As in the case of Software Testing, Testers are constantly dependent on the Instant-Results that Automated Regression Test reports.
This enables them to consistently verify the quality of the application and promote continuous integration and continuous deployment while respecting the Project Timelines.
One of the globally followed practices to address this concern is – Design Patterns.
But you can’t use Design Patterns by just snapping your fingers, can you?
Design Pattern is an innate part of the Software Development Cycle.
The Design Patterns offer a mainstream reusable remedy for known programming complications that often occur in a Software design.
The Design Patterns are a bunch of Best Practices that often results in a flexible and maintainable code.
Knowledge of various design patterns and knowing when to use them will improve your skills in test automation design.
Today, we shall dig into the DOs and DON’Ts of creating an exceptional Test Automation Framework in less than 30 minutes.
First, let’s check out what a typical modularly-programmed Automation Framework looks like?
It starts with a Project, with an option to name the Project.
Inside a Project Name, by default, you have a folder called Source (src) that has the source code of the entire project.
Then there are few predefined libraries.
For instance, if your programming language is Java then you’ll have Java Libraries, like a Compiler that is required for an editor.
Besides this, you could arrange the external libraries separately, in another folder.
Say, in the case, where you need to run the code on a Chrome Browser, naturally you’d need a chromedriver.exe or other external Jar files like TestNG.
So, all of these externally used libraries, that you would need to develop your code, could be placed here.
The primary reason behind this action is to ensure the smooth transition of the libraries along with the code whenever it is transferred from one system to another.
So, even if the new system does not have these libraries there won’t be a tricky situation as such since these libraries are already available within the main project.
Also, you could employ relative paths to call these libraries in the code.
It’s always recommended to have layers or folders within the Source for appropriate structuring.
For example, you could have individual layers for
- Requirement Layer
- Testing Layer
- Application Pages Layer
- Configuration Layer
- Data Layer
- Requirement Layer
- These could be recorded in a simple language similar to the Test cases, which makes it easier to understand – especially for Business Stakeholders.
- If you happen to use Cucumber then you could have the feature files, that use Gherkin language, within this section.
- This could be used to define multiple actions or use pre-condition such as Given, When, and Then to list the Business Requirements in this section.
- This step is situation-dependent, where you could avail the option to have a separate layer based on the project’s requirement.
- This kind of layer is generally used in Behavior-Driven-Development.
- These could be recorded in a simple language similar to the Test cases, which makes it easier to understand – especially for Business Stakeholders.
- Testing Layer
- This is where we include all the Tests in a listed format.
- This is also where we add Steps, Assertions, and Validations.
- It is important to ensure that all of these Tests are Loosely Coupled i.e. their execution output should not hamper the execution of the succeeding tests.
- These Tests should generate Status Reports at the end of their respective execution. They could generate logs or reports for you to understand the execution status.
- Another inclusion that is recommended here is, to have a Master or a Parent Test which shall have all the common functions or declarations that could be used throughout these tests. Post this, you could have your Test Classes extend the Parent Test file.
- This is where we include all the Tests in a listed format.
- For instance, all the tests have some commonalities like
- Send a Status Report at the end of their execution.
- Log the steps.
- Launch the Application.
- Send a Status Report at the end of their execution.
- To perform these common actions, it requires you to write the functions irrespective of the programming language that you are using. Also, while executing the Test Cases it is important to record all the activity information and log them into files or reports.
This file or report could be referred to in the future for any code reviewing purposes.
Hence, it is imperative to implement a logger in the framework and customize it for the requirement. If you are using Java then you would use the Log4j utility for this purpose and if you are using Python then you would need to employ a Custom Logger. - So, to fetch these data while executing the tests, you could do better with having all of this, in a Parent or Master Test rather than writing and including them individually for each Test. This promotes Code
- If the requirement needs the application to be tested across Browsers / Platforms then you’d need to write a code within the Master / Parent Test that records a function reads the User’s Input and launches the browser accordingly.
- For instance, all the tests have some commonalities like
- Now, about Grouping Test Cases with minimum effort or change to the code. This can be taken care of by adding Tags on top of each Test while defining them. The tags could be like “@smoke”, “@regression”, “@login”, etc. It is important to remember that each test must have at least 1 unique tag so that you could run that particular test using the unique tag. You could also pass the tag from the command line while taking them from the input.
- Tagging the tests is a Grouping Mechanism that groups the test cases based on their tags. For example, if there are 3 Test Cases related to Login, then you could have a few individual tags as well and define multiple tags for the test using a comma. You could also define that Login is a part of “@smoke”, “@regression”, and “@login”. So, in the future, if you were required to run only the Login-related Tests then you might as well pass only the “@login” tag, meaning – if only 3 Tests have the “@login” tag then only those 3 would get executed. The others would not.
- Now, about Grouping Test Cases with minimum effort or change to the code. This can be taken care of by adding Tags on top of each Test while defining them. The tags could be like “@smoke”, “@regression”, “@login”, etc. It is important to remember that each test must have at least 1 unique tag so that you could run that particular test using the unique tag. You could also pass the tag from the command line while taking them from the input.
- You may also take the Tags from the end-user during the execution process and then call them in your Master Test File, to decide which Test to run.
- About Reporting – For each individual test, you could write certain functions in the Master File for reporting. Besides, there are a lot of Reporting APIs such as Extent Reports, ReportNG, TestNG, etc. The idea is to write or define the function that locates the path for the report to be created.
- About Screenshots of all the Test Fields – This is again a common and independent function that could be defined in the Master File. Here, you could identify where to store a particular screenshot, store multiple screenshots within a folder, dump old screenshots, and generate new screenshots using timestamps or dates or based on their execution. Simple Java or Python programming codes are easily available on the Internet that you may use to perform this action.
- Now, when do you call this function? You could write logic as to whenever your Test or Assertion fails then you may call the function for the Screenshot where it shall capture the screenshot and save it automatically.
- About Ability to Run from Command Line – Whenever you start your tests, they should always start from the Master Test File and here you could actually take input from the Command-Line. All that you’d need is a function that accepts input from the command line. So, if you’re running the code from an Editor then you could take the input from the config file, or else you could take the input from the command line.
- You may also take the Tags from the end-user during the execution process and then call them in your Master Test File, to decide which Test to run.
- The reason why all the tests need to run from the command line is that it becomes easier to deploy the code on the server. Also, for CICD purposes, if you are required to run it from Jenkins then it is recommended that the tests run from the Command-Line.
- About thread.sleep – It has been regularly observed that a web app takes time to load. Now, this could be because of the internet connectivity issue, among several other reasons. To handle this concern, you need to pause the script, for all the web elements to load. Now, this is a very decent method to guarantee that all your web components are there while you run your tests on them. But it is also important to develop a Dynamic Code that will detect the element if it is available on the very first second or if it is available after 5 seconds then it shall wait for 5 seconds and then move on.
- The reason why all the tests need to run from the command line is that it becomes easier to deploy the code on the server. Also, for CICD purposes, if you are required to run it from Jenkins then it is recommended that the tests run from the Command-Line.
- One approach to delay your script is by utilizing the thread.sleep() function. It pauses your test script for the predefined time. However, there is a significant deficiency in this practice. Suppose you need to launch a page, and you’ve chosen to stand-by time to be 6 seconds, in situations where your site launches a lot quicker, you would’ve burned through a great deal of time. Similarly, if the site loads late, then you’d get the outcome of the execution will fail. So as to maintain a strategic distance from such circumstances, you could use implicit or explicit waits.
- Application Pages Layer
- This is the section that includes all the pages of an application.
- All the actions-to-be-performed, concerned with the web pages, along with their related web elements are included in this layer.
- Here’s an example that every Automation Test Engineer could relate to. Say, there are 4 web elements on the login page of the application, namely – Username, Password, Login, and Forgot Password. These elements should be included within the Login Page. If in the future, there’s a requirement to modify or revamp the Login Page then we should be in a position to accommodate the requirement without having to disturb the entire code. All that you’d need to do then, would be to edit the Login Page file with a different Element ID.
- Ensuring this would mean that your code is Dynamic, Flexible, and Scalable.
- We have already discussed the importance of having Steps and Assertions in tests. A smart practice is to include any action or step, that you perform on a web page, under this section. Say, you have common functions or steps amongst several tests on a web page. You could prevent writing these functions repeatedly by calling this function directly from this section. This prevents Code Redundancy.
- Just have a Master or a Parent Page File which shall have all the common functions or web elements that could be used throughout these tests. Next, have your Test Classes extend the Parent Page file.
- All the actions-to-be-performed, concerned with the web pages, along with their related web elements are included in this layer.
- Configuration Layer
- This section has all the config files like “config.properties”, “.server”, “.env”, within which you could define the key values of the Default Browser along with the “appURL”. The important point to note here is that a code will always read only the “appURL”. It will not read what value the appURL holds. It will only use the key, in this case, the Browser and the appURL, to open a particular Browser or launch the specified URL.
- You may also use a key to define executionPlatform as Local, Cloud, or Headless.
- In the case, where the project has common credentials, this is the layer where you’d need to administer them.
- Data Layer
- Any data related to the project, be it CSV, XML, JSON files, whichever Test Data your system is using – they need to be included here.
- For example, for each test you can have a different file like for Test Case 1 you could have test1.json or test2.json or test1.csv, provided that the code is loosely coupled, thereby making it easy to read and understand.
- Any data related to the project, be it CSV, XML, JSON files, whichever Test Data your system is using – they need to be included here.
Points to Remember:
- Test Automation Framework (TAF) should be Dynamic and Flexible.
- TAF should be Scalable .
- Reuse the code and avoid Code Redundancy .
- Test Cases should be Loosely Coupled .
- Never have 2 Test Cases share 1 function within each other .
- If your Test Case 1 fails then it does not necessarily mean that your Test Case 2 should fail. Test Case 2 should still Launch and operate, provided you’ve followed all the right protocols .
- Commit your Code and write as many necessary comments as you can .
- Add as much data as possible to the report to make it comprehensive .
- Catch exceptions so that you could print them in your Logs or in your Logger File .
- It is important to have command over the Programming Language before jumping into Automation.
Conclusion
The present-day Software Development environment has made the Test Automation Framework a larger and important cog in the wheel of the Software Testing Life Cycle.
By practicing the aforementioned simple yet smart TAF designing points, Software Testing Stakeholders could benefit colossally.
All of this while easily maintaining and successfully executing Software Test Automation Projects.