Testing is a crucial part of the software development life cycle. Testing helps ensure that various aspects of the application work as intended and meet user expectations.
In the world of software testing, two main types often intersect but serve different purposes: UI (User Interface or GUI Graphical User Interface) testing and API (Application Programming Interface) testing.
While both are essential in delivering a robust product, they address different layers of an application. Understanding their differences and knowing when and how to utilize each can be a game-changer in creating efficient and effective test strategies.
In this blog, we will dive deep into UI and API testing, exploring complex examples, modern practices, and techniques that might help QA engineers improve their testing processes.
What is the UI testing?
UI testing focuses on the graphical interface of an application. It verifies that the user interface behaves as expected, ensuring that elements like buttons, forms, menus, and other graphical components function correctly.
The primary goal of UI testing is to ensure a seamless user experience, validating that the end-users can interact with the application as intended.
Key objectives of UI testing:
Validate the visual and functional aspects of the UI.
Ensure that user interactions trigger the correct workflows.
Test cross-browser and cross-device compatibility.
Verify adherence to accessibility standards (e.g., WCAG 2.2).
What is API testing?
API testing is the process of testing API endpoints directly, targeting the underlying services that power the application's business logic. It involves testing endpoints to verify data exchange, business rules, and response formats.
Unlike UI testing, API testing does not require a graphical interface, making it faster and more stable.
Key objectives of API testing:
Verify that APIs function correctly under various conditions.
Test the structure, accuracy, and performance of API responses.
Validate error handling and response codes.
Ensure data integrity and security for sensitive information.
Why differentiate between UI and API testing?
Although both types of testing aim to improve software quality, they address different aspects of an application. Focusing too much on one while neglecting the other can lead to a fragile testing process. Here’s a comparison to illustrate their importance:
When to use UI testing and API testing: A pragmatic approach
In modern applications, both UI and API testing play crucial roles. Here are scenarios where each shines:
When to use UI testing
-
Validating the user experience: UI testing is essential when verifying how users interact with the application—for example, testing a complex signup flow where multiple form validations, CAPTCHA checks, and third-party integrations come into play.
-
Visual regression testing: When you want to ensure UI changes do not unintentionally affect other parts of the interface, automated visual regression tests can catch such issues.
-
Cross-browser and device testing: Modern applications must support multiple devices and browsers. UI tests can validate consistent behavior across Chrome, Firefox, Safari, and mobile browsers.
When to use API testing
-
Isolated business logic testing: API tests are ideal for validating business rules without the overhead of the UI. For instance, testing a payment gateway integration to verify calculations, fraud detection, and error handling can be done entirely through API tests.
-
Early testing in CI/CD pipelines: API tests can run as part of the continuous integration process, catching issues earlier before the UI is even ready. This allows faster feedback cycles.
-
Performance and Load Testing: API tests are more suitable for performance and load testing, where multiple concurrent requests are sent to check the system's scalability and response times.
Hybrid, hybrid, hybrid everywhere….
In today’s world, the term "hybrid" has become ubiquitous. It seems scarcely a day passes without it being referenced in one context or another (hybrid cars, work, etc.). Why not embrace a hybrid approach in software testing?
This is precisely what we’re doing, and it has become one of the most modern practices for testing complex solutions and applications. Both types of testing have their respective strengths and weaknesses. By leveraging the strengths of one, we can mitigate the shortcomings of the other and vice versa. Let’s try explaining it with the following example.
Scenario: E-commerce - verify items in the cart
In our scenario, we must check if items have been added to the shopping cart. To test this scenario, we have to follow the next steps:
Navigate to the e-commerce URL
Add products (items) to the cart
Verify if the products (items) are in the shopping cart
For test implementation we could use only UI testing; however, you’ll agree that it might be slow, and the test could be flaky for various reasons (multiple interactions between HTML elements, HTML element loading time, etc.). To avoid potential speed and flakiness issues, we are going to use a hybrid approach:
Use API calls to add products to the shopping cart, creating the exact setup we need.
Use UI tests to verify that the cart accurately reflects the API setup, ensuring the correct user experience.
Implement POM (Page Object Model) for the cart page, making our UI validations modular and maintainable.
For our “complex” scenario, we need some framework for UI and API testing, so let’s do it using Playwright. Playwright supports multiple programming languages such as Java, JavaScript, TypeScript, Python, and .NET. In this example, we will be using Typescript. For the whole project setup, you could contact [email protected], and I’ll give you access to the GitHub repo.
Step 1: Setting up the Cart using API calls
We start by adding products to the cart using direct API calls. This allows us to control the exact items and quantities in the cart before the UI test begins.
Step 2: Verify that the cart accurately reflects the API setup
Here, we come to UI testing. In the previous step, we received HTTP 200, and we assumed that the items were in the cart, but we had to verify it. When we get to UI testing, numerous user interactions and page navigations can quickly make code cluttered and hard to maintain.
We use the Page Object Model (POM) to address this, which enhances code readability and maintainability. Let’s create a CartPage class that contains functions for navigation and getting cart item details.
Now, we can use CartPage in our test UI cart validation.
After the cart is set up, we navigate to the cart page in the UI to confirm that the contents reflect what we set up via API. The test calls getCartItemDetails to retrieve each cart item’s details and then performs assertions based on the returned data. The POM handles page interactions, while the test itself is responsible for validation.
Step 3: Cleanup with afterAll
In afterAll, we perform cleanup by deleting the cart’s contents through an API call, ensuring our tests leave the application state clean for future runs. In my project, BE is served by a JSON server. By default, JSON Server doesn’t automatically support DELETE requests on collections (like /cart) to delete all items.
Instead, it typically requires an ID to delete a specific resource (e.g.,DELETE /cart/1). In the real use case, you’ll probably be able to use DELETE requests on the whole collection.
Benefits of the hybrid approach with POM
Efficiency: Using API setup avoids time-consuming UI interactions, while POM centralizes UI interactions, making tests easier to maintain.
Reliability: API setup eliminates potential UI flakiness, ensuring our tests are more stable and resistant to frontend changes.
Maintainability with POM: As the UI changes, POM encapsulates selectors and actions in one place, simplifying updates.
The above example is just one of many. One of the most common uses of a hybrid testing approach is with repeated logins. To avoid repeatedly entering the username and password in the beforeAll hook, send a request to the login API with the username and password to retrieve the authentication token. Then in the test use await browserContext.addCookies() method to set the token as a cookie, and now you are able to test without repeatedly entering the username and password.
Conclusion
Using a hybrid approach with Playwright and TypeScript—or any framework you prefer—brings a new level of efficiency and reliability to test automation. By leveraging API calls for setup and validating the results in the UI, we streamline our tests, focusing on key user interactions without the overhead of lengthy UI setup steps. Implementing a Page Object Model (POM) further enhances the maintainability of UI tests, ensuring they are organized and resilient to change.
This hybrid strategy is ideal for complex workflows, where backend consistency and frontend accuracy are both critical. Incorporate this model in your test suites to optimize test speed, improve stability, and enhance overall coverage, ensuring your application/system is resilient, scalable, and user-friendly.