Unit Testing is one of the essential tools every developer should use. However, I have seen many projects making it difficult to carry out this as a practice. There are many reasons for this. For example, some might say they need to focus on feature development, and writing Unit Tests is quite an additional effort. Others might say it’s challenging to test the code that we write because it’s complicated. In either of these cases, you are missing the point.
I can recall a saying that, think of whether you have time to write Unit Tests at first place or spare time to work on the same code twice later on due to bugs and issues.
First of all, let’s find out what are the common mistakes we do, which makes it challenging to test our code. Since I will be focusing on the frontend testing, one of the most common issues in front and code that I have seen is that it’s challenging to test the UI components. The main mistake I have seen here is that if the components become too smart, having dependencies on different code segments such as API calls, loading data, handling event operations, dealing with business logic. I have seen many developers getting frustrated writing in tests for these “heavy” components.
The easiest solution for this is to separate the components into logic and presentation. Having this separation allows us to test the view components for presentation logic, event handling, and visualization while focusing on testing other pieces of business logic separately.
Making your components testable is also a good way to make sure they are independent, reusable, and shareable. That’s absolutely crucial, especially when sharing components between projects using popular tools and platforms like Bit (Github). As a matter of fact, Bit will render and test your components in isolation before pushing them to your shared collection, to make sure they are truly reusable — otherwise, there’s no point in sharing them.
From my experience, when I talk to teams, those who don’t practice Unit Tests properly (using coverage as a measurement) either start late or started in the beginning but not followed it as a practice. There could be many reasons behind this, but I will present a few examples, which will help you to see whether your case fits one of these.
How we test our code when developing
In my opinion, unless we follow Test Driven Development (TDD), we likely develop the functionality by keeping the frontend loaded to the browser. We have the focus to visualize the functionality and interactions with the user interface (UI) at the heart of frontend development.
Then only, the focus comes to write Unit Testing, once the functionality is working.
Here the main challenge we face is that Unit Test writing is an additional step that we do after completing the functionalities. This approach creates the mentality that a Unit Test is extra work on top of working functionality.
This mindset creates an issue for product owners, as well as developers, to think about it as an overhead.
However, if you follow TDD, it’s the other way around. Since we write the test cases upfront, we don’t necessarily have to look through visualizing the changes all the time and has a different mean of verifying the code while developing. Here, writing code to pass the Unit Test Case itself is the primary verification.
Therefore I feel, having the practice to follow TDD is a crucial step in practicing Unit Tests.
How often the Unit Tests Runs
You might wonder how does The execution of Unit Test affect writing Unit Tests. For instance, imagine that you have a complete test suit. But you run it once in a while. What happens eventually is that developers won’t see its value often enough. Besides, even when we figure out a test is breaking, it’s too late. Then the responsibility of who has broken the Unit Test slips through our fingers.
Therefore it’s essential to execute the Unit Tests at least at Pull request build time. Following these DevOps practices will ensure that each new code block added is tested against the Unit Test suit. Even if there’s a need for change for this case, the developer will do that before the code is merged.
How often do you measure the coverage
Similarly to test execution, this is also an import measurement both psychologically as well as a practice for developers to know whether they are writing enough test cases to cover the code they have written.
In this case, I believe having a dashboard to see the Unit Test coverage is not enough. But if we could establish a measurement when we add new code to verify coverage, that would be a better alternative to get instant feedback. We could even establish rules requiring that the coverage should be, for example, about 80% for any new code added. The best place to add this evaluation is the Pull Request, build process.
Since developers get instant feedback on their code in terms of Unit Test coverage, they are empowered to take action to keep up with the coverage at all times.
If you’re already in the vicious cycle, the best way to come out of it is to establish these practices discussed above. Since both of these practices talk about running your test frequently to verify the new code added as well as to test the coverage of new code, you can establish the procedure for any new code additions or modifications.
It’s highly unlikely you can get a good enough time to complete the coverage for old code (unless the project is new) at once before you write any new code. So don’t think this will ever happen, since its not vible in terms of business value.
In the meantime, you can take periodic measurements to add coverage for the old code with a strategy in mind. And most importantly, you need to make the buying of all the developers to commit to this practice. It is equally important not to consider it as an overhead and communicate to all stakeholders the importance of writing Unit Tests. Otherwise, especially non-technical people might see it as an additional effort that could be used to develop new functionality.
Many values come with Unit Tests. If done right, it will help to reduce defects in code, acts as a shield to verify existing functionalities are not broken when we do code refactoring, as well as to keep overall productivity high.
And you all know that when we see a code with a good Unit Test coverage, how confident are we about the code.
So let’s start on filling these gaps and come back to the track, by establishing the DevOps practices as well as writing proper Unit Tests, following Test Driven Development.
If you have any questions, feel free to post them in the comments below. Cheers!