26 November, 2020
When creating automated tests, it is often necessary to repeat certain steps to test a given functionality. In the following article, I would like to share with you my approach to test automation best practices. This method significantly reduces the problems and risks resulting from the implementation of tests using only UI elements.
Many software development projects that I have dealt with included the implementation of tests only on the UI layer. This translated into the duplication of basic steps (related to, for example, registering a new user) and had several disadvantages, most notably:
- the duration of testing was noticeably extended,
- if an error was found in the UI layer at the beginning of the test scenario, the test implementation made it absolutely impossible to test the functionalities available at a later stage.
Optimizing test automation – the action plan
Step 1: Get to know your application as early as possible. In full!
When starting a new project and testing tasks, it sometimes happens that you only pay attention to the application layer that comes into contact with the end-users. However, it’s worth thinking about what’s going on in the backend. There, you most often deal with applications based on REST API. Knowing this, you can go a step further and analyze the API documentation available in a tool called Swagger (btw. a final proof that IT people have swagger too! 🕺). Having the UI layer in one hand and the API in the other, you can start designing E2E tests.
Step 2: Prepare the E2E test architecture, also for API.
When writing code, regardless of whether it is a new application or an automated test, it is good to take care of its order, use good practices and design patterns. Among the automated tests, the most popular of them is Page Object Pattern, but nothing will stop you from using a different approach (e.g. Screenplay Pattern is also quite popular). The most important thing is that the adopted approach allows you to easily improve the existing code with new tests without the need for constant refactoring.
If you decide on implementing automated tests using the Page Object Pattern, you can create the following structure:
endpoints directory, put the implementation of support for all endpoints that you will use in your automated test. In
pages – implementation of methods related to functionality on individual pages in the application’s UI. Finally, divide the
integration directory into two subdirectories:
ui, where you will place integration tests for the appropriate layers.
Step 3: Implement automated tests for single functionalities, without dependencies.
Imagine a financial application with a registration screen, login and history of all transactions. Our goal is to implement scenarios that will cover the following functionalities:
- New user registration,
- Logging in with an existing account,
- Viewing transaction history.
Create the files in the endpoints directory…
…in which you’ll include support for endpoints used for registration and logging.
Next, create the files in the
…in which you will implement support for each process using the UI layer only.
INB4: I deliberately don’t provide a ready implementation in the above files. Not because I’m protecting my precious test automation method Gollum style. Much depends on the selected framework, test automation tools, your programming experience and knowledge of test automation best practices – this way you can adjust the code to your preferences and possibilities.
What interests us the most here is the implementation of integration tests. In the case of API, you can arrange integration tests in a fairly simple way. First, create the specification files for the API…
…by filling them with content. Start with,
loginAPI.spec.js using the registration and login endpoint.
This set allows you to verify the registration and login process via API. As you can see, both test scenarios are completely independent of each other.
Thank you, next. Prepare tests for UI:
This implementation looks a bit similar to the API but apart from
endpoints, you also use pages. For
For testing the functionality related to displaying the history, in order not to duplicate the login process, try to use the login endpoint that, for example, returns an
accessToken. It’s quite likely that you just need to set it in a cookie in your application to view the page as a logged-in user.
transactionHistoryUI.spec.js test code could therefore look like this:
If you wanted to implement a test verifying the details of a specific transaction, you would first add support for a new endpoint to obtain a transaction list via API. Then, on the UI layer, you would implement a new page that would receive the ID of a specific transaction as input and then display its details.
💡 Read more: GraphQL vs REST – the battle of API designs
Do you rely on API in E2E tests for UI?
What we managed to achieve here, apart from the architecture that is very easy to maintain, is the code of automated tests where each test on the UI layer verifies only the part of the application from the user side for which it was defined.
All previous stages in a given scenario were handled exclusively on the API layer. Firstly, it shortens the duration of the tests, and secondly, reduces the risk of their instability.
Thanks to this approach, you are able to easily implement subsequent test cases while maintaining the principle of full independence of each automated test. An additional advantage of this solution is the ability to connect UI and API tests separately in the deployment process of both applications.
Of course, I am aware that we are not able to use the API for every functionality, but where possible, I think it is worth reaching for a similar solution. As you could see for yourself, test automation best practices advantages cannot be denied.
Good luck! 🤞