Optimizing Software Quality: Unit Testing and Automation
Explore unit testing, which checks individual functions to catch regressions early, and automation testing, speeding up overall testing.
Join the DZone community and get the full member experience.
Join For FreeUnit testing is the first line of defense against bugs. This level of protection is essential as it lays the foundation for the following testing processes: integration tests, acceptance testing, and finally manual testing, including exploratory testing.
In this article, I will shed some light on what differentiates unit testing from other methods and will bring examples of when we can or cannot do without unit testing. We'll also touch upon automation testing, which plays an important role in ensuring code reliability and quality.
Unit Testing
The idea of unit testing is to write tests for every non-trivial function or method. This allows it to quickly check if recent code changes have caused regressions, meaning errors in parts of the program that were already tested, and it also makes it easier to detect and fix such errors.
When Unit Testing Is Excessive
Any long-term project without proper test coverage is destined to be rewritten from scratch sooner or later. Unit testing is a must-have for the majority of projects, yet there are cases when one might omit this step. For example, you are creating a project for demonstrational purposes. The timeline is very tough. Your system is a combination of hardware and software, and at the beginning of the project, it's not entirely clear what the final product will look like. The software will operate for 1-2 days during the exhibition or presentation. In this case, there is no need to implement unit testing.
Another case is when you are working on an advertising website, or simple flash games, or banners, which involve complex layouts, animations, and a large amount of static content. All of the above serve for the presentation.
If you are building a simple business card website with a set of static HTML pages and a single email submission form, no unit tests are required. The client will most likely be satisfied with this and won't need anything more. Most likely it will be faster to check and test everything manually.
Unit Testing Implementation
When planning the unit testing, bear in mind that your aim is to ensure that your unit testing code coverage exceeds 80%. This means at least 80% of your codebase is executed when running your unit tests. For these purposes, I recommend tools like JaCoCo for Java or Istanbul for JavaScript. So, to start incorporating unit testing into your development process, try going through the steps below.
1. Choose an Appropriate Testing Framework
Select a framework that fits your needs rather than reinventing the wheel. For instance, many .NET developers use MsTest because it comes with Visual Studio, but NUnit or xUnit might offer better features for your project.
2. Decide What to Test
Not all code needs testing. Simple, dependency-free code might not require tests, whereas complex code with many dependencies might benefit from refactoring before testing. Focus on testing complex, algorithmic code, and interdependent components to ensure clear interaction and integration.
3. Maintain Consistent Test Structure
Use the Arrange, Act, Assert (AAA) pattern for clarity and maintainability.
4. Test One Thing at a Time
Each test should verify only one aspect of the code. For complex processes, break them into smaller parts and test them individually.
5. Handle Dependencies With Fakes
Replace real dependencies with fake implementations to avoid testing unnecessary components. Use stubs for predefined responses and mocks for verifying interactions.
6. Use Isolation Frameworks
Use existing frameworks like Moq or Rhino Mocks to create mocks and stubs instead of writing your own. This reduces errors and maintenance overhead.
7. Design for Testability
Initially write code with testability in mind. Use dependency injection, avoid direct instantiation of objects within methods, and minimize the use of static methods and constructors with logic.
8. Refactor Legacy Code
If dealing with untestable legacy code, start by refactoring small, manageable parts and cover them with integration and acceptance tests before writing unit tests. Gradually expand this process to larger parts of the codebase.
Automation Testing
The name of this method is self-explanatory: in automation testing the test cases are executed automatically. It happens much faster than manual testing and can be carried out even during nighttime as the whole process requires minimum human interference. This approach is an absolute game changer when you need to get quick feedback. However, as with any automation, it may need substantial time and financial resources during the initial setup stage. Even so, it is totally worth using it, as it will make the whole process more efficient and the code more reliable.
Automation Testing Implementation
The first step here is to understand if the project incorporates test automation. You need to ensure that the project has a robust test automation framework in place. In turn, automation engineers should be proficient with the tool stack (e.g., Selenium, Appium, Cypress) and follow established automation guidelines.
1. Automation Coverage Compared to Manual Tests
Strive for a high percentage of test cases to be automated, ideally over 90%, to maximize efficiency and reduce the reliance on manual testing.
2. Project Overview and Automation Implementation
Automation testing is always a large project involving multiple teams developing a shared product, with Manual QA testers present in each team. Testing focuses on both frontend and backend aspects.
3. Understanding the Project
First, we need to understand the product's purpose and its users. This helps prioritize automation efforts. For instance, if the product serves businesses, focus on testing legal compliance and payment transactions. For consumer-facing products, prioritize key operations like card-to-card transfers and service payments. Automation should be applied comprehensively across the entire product rather than just individual teams.
4. Identifying Key Stakeholders
It's crucial to be familiar with all stakeholders since interaction with them will be necessary. Key people include:
- Product owners: They are the clients of the automation and define its requirements.
- QA engineers: They are the end-users of the automation tools, and their satisfaction is a measure of success.
- Manual testing leads: They help organise the process and coordinate with manual testing.
- Frontend development leads: They influence the stability and quality of automated tests.
- Procurement specialists: They handle hardware allocation, mainly for server equipment.
5. Understanding Teams
Gather information about each team's project scope, whether it covers frontend, backend, or both. Understand how QA teams test their sections and their familiarity with automation. Identify testing challenges and prioritize areas for automation.
6. Formulating Automation Requirements
In the majority of cases, we aim for a classic approach without innovative solutions:
- Programming language: Java, to facilitate hiring specialists
- Frontend testing: Use Selenium.
- Backend testing: Use REST-assured for REST interactions.
- Database testing: Go for standard Java libraries
- Automating tests: Choose Cucumber for both training manual QA testers and reducing costs.
- Reporting: Last, but not the least, use Allure for attractive and informative reports.
7. Demo and Onboarding
Conduct a demo for all stakeholders, including Product Owners, QA engineers, developers, and analysts, focusing on clarity. Begin with a front-end team to create visible results. Develop 5-10 automated tests, record them, and show the results using Allure for graphical reports. Illustrate the automation infrastructure, main goals, and effects, and compare manual and automated testing.
8. Preparing the UI for Automation
To ensure reliable and stable automated tests, add “data-test-id
” attributes to UI elements centrally, with the cooperation of front-end leads and product owners. This practice greatly enhances test reliability by insulating tests from changes in UI element positions or content.
9. Developing Automated Tests
Distribute tasks among automation testers. Create a project framework for automation using templates. Prepare Cucumber steps for frontend testing, make these steps reusable across projects, and set up Selenoid and Jenkins. Integrate teams into automation by setting up repositories, creating Jenkins jobs, and training QA in Cucumber, Git, and development environments.
QA manual testers will then write their automated tests, which will be reviewed and integrated by automation engineers. The final Cucumber steps development will occur during spare time in the sprint. At the end of each sprint, showcase results and announce new features in the product demo.
Conclusion
As you can see unit testing and automation testing are complementary approaches. By using them to identify defects daily, you can reduce regression testing time at each stage. Besides, this will gradually lead to a faster release of the product into production, saving time and resources.
Opinions expressed by DZone contributors are their own.
Comments