1. Naming Convention
This is indeed one of the thumbs rules to keep in mind as we move from manual to automation or in fact writing code in any programming language. Following proper naming conventions helps in easier understanding of code and maintenance. This naming convention implies on variables, methods, classes and package. For example, your method name should be specific as what it is intended for. A ‘Register_User()’ method portrays the method displaying user registration within that method. Clearly defined method names add to the easy maintenance and readability of the script. The same extends to the variable naming. I have noticed many people mentioning variable as a, b, c etc or even Web Elements as Weblelement1, Webelement2 and so on. This gives no clue to the user seeing the variable name as what it intends to do.
Below is an example showing when naming goes wrong:
The above code shows how ‘method1’ gives no clue to the user as what this method exactly does. Also, all web elements are denoted via web1, web2…and so on. User cannot identify which web element captures which field.
A correct way of representation can be marked as follows for the same above code:
Here the method name ‘Register_User’ clearly defines user through name indicating this method contains code related to the registration of the user. Similarly, all web elements or variables are provided with names which relate to the captured fields used for the defined intent.
Usually, using camel casing for writing down methods or variables is usually encouraged for its better clarity in terms of readability and maintaining the script.
2. The Three R’s-Reduce, Reuse and Recycle
It’s important to ensure your methods are broken to the smallest chunks of user scenarios. They should cover simple and single flows. Do not overcomplex your methods with multiple functionalities covered in a single method. For example, a login feature needs the user to be registered on the application. Keep your register feature into another method and if required call that method in your login method. Reducing the complexity of the methods leads to easy maintainability of the code.
Also, reuse your methods wherever required, do not copy paste the same code in different methods. This leads to unnecessary duplication and redundancy in the code. Increasing the lines of code does not means you have written a good code. Refactoring and optimizing your code is a key to a writing stable, robust and better automation code.
Recycling is also another useful tip for writing better automation code. I have experienced people who automate the legacy system, do not tend to change the existing method in the automation framework and rewrite another method whenever there is a change in existing functionality. This simply makes the framework as brittle. Always update the existing methods whenever the flow changes, though it has its own challenges, where the new user may not be aware of the dependencies the method may have, but I believe we should always counter things for the longer perspective than achieving those shorter goals.
Below is an example of how the login code is being simplified into a small chunk of functionality and another registration method is been used for easier simplification of the whole process.
3. Structure Your Tests Well
Well, this is indeed one of the major actionable insight to ensure better automation code. It is not only easy to understand but does not take much effort in maintenance. Structuring your tests with the help of framework adds value to your work and reduces the maintenance effort in the long run. You can control the flow of your application via the use of annotations provided by frameworks like JUnit and TestNG. For example, using an annotation like @BeforeClass can help you direct your time-intensive activities like connecting to the database, setting up the browser etc related code in this method with @BeforeClass annotation associated with it. This helps an automation tester known right away as what exactly that method does and when it is called. Just imagine your setting up process is clear and sorted out from the other pieces of your code. Similarly, an @AfterClass annotation helps you perform clean up activities like disconnecting to the database, closing your current browser sessions etc.
Below is an example highlighting a better structuring approach is been shown through the TestNG framework:
Taking a decision of what annotations should be associated with which test method is important. With clear dependencies and priorities defined the tests and code can be structured based on the flow of the application.
4. Thorough Validation Of Your Tests
Being a QA you know, it is all about validating your expected and actual meets, the same stands for your automation code. If your script does not talk in terms of validation, creating one will never make sense nor be of any essence. Ideally, every user action should be validated as are your test case steps, whether it is validating the visibility of an element, typography, textual representation, redirections to a page or any kind of visual validation or even if it’s about evaluating the results from the database.
Even if your validation fails to make sure, the failure message is also displayed so that one can find out what went wrong. The biggest mistake we make in terms of validating our code is writing from the terms of ensuring the validation is passed. We never contemplate what may happen if the code fails or does not perform the expected, what would be needed to proceed ahead.
If you wish to break the test as soon as your validation fails and jump to the other test one can use hard assertions whereas if you wish to validate multiple checks on the same page, one can opt for soft assertions. To decide which assertion to use completely depend upon the use case.
Below is an example of assertions performed on a login page. In this different method are created where the user is logged in with valid credentials and then another method ensuring the user is not logged in with invalid credentials with an error message displayed.
There could be different approaches of covering your multiple validation checks, either you can opt to make different method for each validation like I did above or you can choose to make all validations in a single method under the try-catch block.
5. Sleep Does Not Improve Stability
The biggest myth we tend to believe especially when we are new to this automation business is by providing an ample amount of wait to our script necessary or unnecessary will lead to executing our script smoothly. On the contrary, it makes our script flaky, and increase the overall execution time. The major problem with this type of static sleep is, we are not aware of the load of the machine on which tests are run and hence these may lead to timeouts. Therefore thread.sleep should be avoided for maintaining better automation code. A better approach of using wait to your scripts is through condition binding, wherein the script can wait like a human till a certain condition is met. For example, waiting till the certain element is visible or not.
The explicit and fluent wait is more adaptable as an option to develop better automation code.
6. Making Your Tests, Data Driven
Testing becomes more effective when tested across multiple forms of data, similar is true when writing better automation code for testing a web application or any other software. In automation, the key is to test your test code through multiple forms of data rather than writing different test scripts for each of those data. This is easily achieved via the data-driven testing framework. It helps to store the test data input into an external database such as CSV files, excel files, text files, XML files or even ODBC repositories. This data is called into your scripts and run across the same test code again and again. This helps reduce redundancy and faster execution in comparison to manual efforts. This approach makes your tests more realistic as you always have the advantage of changing your test data and running it over and over again on the same test code, thereby helping discover new bugs.Another benefit of this approach is it leads to the reduction in the number of test scripts you may have to add, speeding up your test cycles.
Keeping in pace with it, it also helps in easy maintainability of the scripts. All hardcoded values in your code tend to break in case of any application changes. An easier way to achieve this is to make all your hardcoded components as variable driven. For example, all locators can be kept out of the code, by storing their respective values in an excel sheet and calling them in your script. In case, any of your locators get broken, one needs to just change the locator value in the excel and need not to touch the script at all.
A basic example of data-driven testing is :
The above code shows data been pulled from excel for different login credentials. The same can be extended for Xpaths also where the XPath values can be pulled from excel also. Here the key point to address through data-driven approach is to remove the hardcoded values from our code, making it variable oriented and along with it running the same piece of code across multiple sets of inputs.
7. Don’t Miss Out On Reporting!
Automation code won’t do good if it does not report the result to you. In order to optimize your work as an automation engineer, it’s important to know which test code passed and which failed accompanied with screenshots. The best ROI you can show your stakeholder is via reporting. Sharing those detailed reports provide visibility and reduce your time on verification of your test execution scripts. You can achieve reporting through various techniques like TestNG HTML report generation, JUnit report generation or via using extent library.
The below code shows an example where post completion of the login functionality a screenshot has been taken as a proof of validation pass and below is a sample of the TestNG report generated post-execution:
8. Don’t Forget Cross Browser Testing!
All web applications today support multiple browsers and versions. It’s important that your code should target multiple browsers rather than making them for a specific browser. Running code on a specific browser takes away the cross browser compatibility of your application. Perform cross browser testing to ensure your applications offers seamless user experience across all the major browsers, we can extend our automation for this testing. Frameworks like TestNG helps to easily executing a test across various browsers.
Below is a code displaying how to run automation code on multiple browsers via TestNG
The above code shows a method which takes a browser as a parameter where different browsers drivers are set up. Using TestNG XML file we have passed parameters as different browsers on which the code will run through for the login feature on both firefox and chrome.
That was all from my end. I hope these tips will serve as useful actionable insights for writing better automation code. Feel free to share the tips that helped you deliver better automation code. Cheers!
Closing the article with the below quote from Rod Michael
If you automate a mess, you get an automated mess.”
Author: Sadhvi Singh