What are unit tests or 'unit testing'?
Unit testing is the practice of writing and executing automated tests to validate the correct functioning of code units. It detects errors early, improves software quality, and builds confidence.
Unit testing is the practice of writing and executing automated tests to validate the correct functioning of code units. It detects errors early, improves software quality, and builds confidence.
Unit testing, also known as "pruebas unitarias" in Spanish, is a "good" practice in software development. It involves writing and running automated tests to verify the correct operation of individual code units, such as functions, methods, or classes. The goal of unit testing is to identify and fix errors in the code early on, ensuring the quality and expected behavior of each code unit in isolation. These tests are typically quick to run, providing confidence as the software development progresses.
Let's explore the main reasons why unit tests are highly necessary:
1. Ensure Code Does What It Should
Perhaps the most important reason for developers is to know whether their code does what it's supposed to do. With unit tests, you can test and demonstrate that your code functions under specific conditions and with different parameters. When you're dealing with classes and other methods, some unit tests may initially pass but fail when code changes occur. In general, unit tests take precedence over functionality, unless specifications change.
Another reason unit tests are useful is when something goes wrong in the application. If an end user reports an error, the support team, often unaware of the functionality, may attribute it to a development (code) problem. If you don't have unit tests validating that requirement, it's likely because it wasn't requested, indicating a business or Product Owner error. To avoid being told that you didn't do your job, you can check if the code is genuinely breaking, if it's a user error, a design flaw, or unclear specifications. Additionally, it simplifies debugging the application to find the problem.
2. Collaborative Teamwork
If you work in a squad or on a project with multiple concurrent squads, someone might be changing the code "without validation." Even today, most developers upload code without any QA or testing process, let alone containerization. We recently encountered a severe problem in a marketplace serving thousands of daily purchases, where the annual investment in development reaches staggering figures. The website was down for several hours daily, always at night, due to a coding error caused by an integration with a sales analytics system that triggered unaccounted-for scenarios. Since there wasn't a single unit test, it took a long time to fix. Considering hundreds of lost sales every hour and the month it took to correct it, it was a business and developer nightmare.
With unit tests, you could prevent these problems. Writing code with unit tests allows you to set up CI/CD (continuous integration and continuous deployment) that halts code deployment to a production environment as soon as an issue arises.
Another key factor in having unit tests that "cover" most errors is that the Product Owner has documented all the acceptance criteria for each user story. These serve as the foundation for the developer's unit tests.
3. Test Your Code Without UI
When you need to test if something works, you create a proof of concept or PoC for short. This involves creating a small project dedicated to the element you want to test. For instance, in a large project with many team members, you can't wait for the entire release to be finished to test your functionality. Additionally, this approach doesn't disrupt or contaminate the main project.
Using unit tests of this kind also improves performance and time efficiency. You don't need a UI to validate a function or service that will be consumed by a web application; it can be tested automatically through calls.
4. Connect Processes - CI/CD Pipelines
Continuous integration and continuous deployment (CI/CD) pipelines are crucial when working on large projects. They help you code and deploy much faster. Services like Azure DevOps, Gitlab CI, and other cloud services facilitate setting up, configuring, and executing CI/CD. In our methodology, this task falls under the responsibilities of the DevOps team.
Another significant advantage of these pipelines is that you can run unit tests during CI/CD. The pipeline first fetches the code from the project repository with Git, compiles it, and runs all the unit tests. If any test fails, the entire process stops and notifies you.
This is essential when working in a team. The Agile coach will suffer less, the Product Owner will have less panic when it's time to present the release, and the client will always have a more "resilient" product.
5. Documentation
Unit tests are essentially user scenarios, as we mentioned earlier. They derive from "Acceptance Tests" for "User Stories," which outline what happens when certain actions, parameters, or situations are used. Therefore, you can use unit tests as documentation. They tell you what methods do and how they react to certain situations or what types of exceptions are thrown.
This is the documentation that Agile suggests because it adds value to the client and developers who will maintain the code.
When starting a new project in which you're going to participate, the first thing we do is read the unit tests. They typically provide a clear idea of what the code does and how it works. As is often the case in most projects, if there are no unit tests, we try to persuade the team and the client to dedicate the necessary time to create them; otherwise, it's practically impossible to tackle the work with confidence.
6. Code Coverage
What happens if you don't test all of the code? We introduce a new term: "code coverage."
Code coverage is a metric used in software development to measure the amount of code executed by automated tests. It indicates what percentage of the source code has been tested during test execution.
Code coverage is expressed as a percentage and provides a measure of how thorough tests are in terms of covering all possible code branches and paths. A high code coverage percentage suggests that most of the source code has been executed and tested, implying greater confidence in software quality.
Most developers say that at least 80% of their code should be tested. We believe that less than 90% offers very little guarantee.
However, it's important to note that code coverage alone doesn't guarantee the absence of errors, as it doesn't evaluate the functional correctness of the software. It's a complementary measure that helps assess test quality and provides information about the effectiveness of the suite of automated tests.
7. Readability
Conducting unit tests through specifications makes the code easier to understand. The tests become documentation, and you get better code in return. Thinking and writing tests first and then developing the code is a key factor that promotes analysis and improves product performance.
8. Test-Driven Development (TDD)
This leads us to Test-Driven Development (TDD): it's a methodology in which automated tests are written before implementing the code, following a "Red-Green-Refactor" cycle. This ensures that the software meets the expected requirements and functionalities, promoting code quality and maintainability through incremental iterations.
First, you write an automated test that initially fails, representing the "Red" state. Then, you implement the minimum code necessary for the test to pass successfully, reaching the "Green" state. Once the test passes, you can proceed to the third step, "Refactor," where you improve and optimize the code without changing its external behavior. This approach promotes modularity, quality, and code maintainability, as any changes to the code must remain compatible with existing tests.
In Conclusion
These are our 8 reasons for creating unit tests, and there may be more, or you might disagree with some. We would love to hear your opinion. We know there's a debate about their application in frontend development, and we'd like to learn about your practices for addressing it.
The most important thing is to keep your code "in shape," free of bugs, and easy to read. Even if it's not 100% bug-free, you can aim for at least 90%. Unit tests are not a burden, they don't take much time, and they are incredibly useful. Getting used to them takes little time, and the improvement in quality they bring more than compensates for any effort required. They make our lives much easier, and there's no justifiable reason not to use them.