This blog post read about unit tests, components of the testing pyramid, and how test-driven development saves time and costs.
Has your development team ever told you they won’t write unit tests as it takes too much time? Or you found yourself in a situation where the test coverage of the code was lower because software developers wanted to make everything cheaper and faster?
Undoubtedly, the testing unit is time-consuming, and it takes more time than writing the code without unit testing.
In the rush of sprints and deadlines, unit testing is often skipped. As a result, you risk bugs in the code and defects that can negatively affect your business goals. But test-driven development can help you avoid the headache, save lots of time and money in the long run.
But first of all, let’s answer an existential question: why do we need testing?
Actually, for several reasons:
- To ensure that the product we develop meets our client’s requirements and expected outcomes.
- Check that we hadn’t broken anything in the system while introducing changes to the codebase.
When the software becomes more complex, identifying bugs can be more difficult, time-consuming, and boring. So, to make fewer mistakes and accelerate the entire development process, we use automated testing, where both developers and QA engineers write tests.
As you’ve already guessed, unit testing is part of the automation process.
This blog post will show how unit testing can benefit your business and share the insights gained in 5+ years of software development testing. But before it, watch our 10 minutes video.
WHAT IS UNIT TESTING?
As the name suggests, unit testing revolves around the concept of “unit,” which means a small and isolated part. Unit testing is one of the many different types of automated testing. There’s no formal definition of “unit testing” as such. Still, Wikipedia defines it as “a software testing method by which individual units of source code—sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures—are tested to determine whether they are fit for use.”
In other words, unit testing is a method when the smallest part of an application is being verified independently from other parts.
Unit testing doesn’t come cheap. However, an excellent test realization will pay off in detecting and fixing bugs quickly and delivering higher product quality.
By and large, what’s important to remember about unit testing, is that it takes some time but, at the same time, saves time on testing during the development process.
So when does your project require unit testing?
- You have complex features.
- Your project will scale and grow over time.
- You want to predict your project’s time to market.
- You don’t want to spend a lot of costs on fixing bugs.
The more of these points match your project, the more you need unit testing.
HOW UNIT TESTING SAVES TIME & COSTS
Given that software developers write unit tests themselves, it’s faster to create and easier to maintain them. You get a return on your investment each time developers compile their code.
Test-driven development encourages developers to create unit tests before they start writing the code. It allows you to automatically check the code for defects, thus preventing bugs instead of fixing them.
In other words, with the help of unit testing, you can find errors at the unit level earlier on, so the cost of fixing them will be much lower.
A case study involving software engineers teams from Microsoft and IBM concluded that “teams experienced a 15-35% increase in initial development time” when using the TDD technique. They also found a quality improvement, and in the long run, TDD saves the time that would have been required to fix problems.
Boby George and Laurie Williams, both working in the Department of Computer Science at North Carolina State University, ran an experiment where 24 programmers were split into two groups: TDD and the linear approach. As a result, “92% of developers believed that TDD yields higher quality code, 79% thought that TDD promotes simpler design and 71% thought the approach was noticeably effective”.
These statistics give a strong indication that test-driven development leads to higher quality code and simpler design.
Further in this article, we’ll describe more ways your project can benefit from unit testing.
HOW TO WRITE GOOD UNIT TESTS?
But for now, let’s focus on the anatomy of a unit test. A suite of quality unit tests allows you to refactor code without breaking things and adding features without creating new bugs.
Simple: Keep the test code simple. Avoid "if" statements and stick to the AAA (Arrange - Act - Assert) pattern.
- Arrange - set up a test environment.
- Act - take actions that are required for the test.
- Assert - check the results of the test.
This pattern improves the structure of the code and makes it easier to read and understand. Make sure always to start writing a test with the Act element.
Isolated: Never write tests that depend on test cases. Otherwise, you risk getting confused about which test in the chain has caused the failure. By creating isolated tests, it's easier to keep your tests focused.
Fast: Most unit tests should take only a few seconds each to run. If they're taking several minutes or longer, then they affect the team's productivity, and developers will be less likely to run them regularly and consequently. This ultimately reduces the value of the unit tests.
Well-structured: Each developer reading another developer's code should see what each unit test is doing. If tests are hard to read, developers are more likely to misunderstand them, leading to bugs. But that's not the only reason we advocate creating well-structured and readable tests. Unit tests form code documentation. So for the team to reap the rewards of all the specs, they need to be readable. Some tips on how to make your test better-structured:
- Give each test a descriptive name (including the code being tested and the expected behavior).
- Follow good code formatting conventions.
- Use meaningful variable names.
Reliable: As mentioned above, keep unit tests simple and use clear naming. A unit test must be failure-free no matter how many times you run it. If a test fails when it should pass, then you can't trust the result. So what can cause test unreliability:
- Have interdependent tests;
- Run the test by relying on assumptions about the system or environment;
- Waiting for something to finish in the system or environment before checking the tests.
It's worth noting that unit tests are written by developers who have a deep understanding of the inner workings of system functionality. So if the test fails, the reason might be the required changes or implementation problems.
The test pyramid was first mentioned in Mike Cohn's book Succeeding with Agile. He recommends that applications should be covered by unit tests which is the foundation of our tests. Then we write integration tests, and the peak is made from UI tests. These are all of the type functional and automated tests.
Very simple description of three levels of testing pyramid:
- At the bottom of the pyramid is unit tests. These are the majority of tests you have for your codebase. As already mentioned, unit tests are great for testing small pieces of a codebase. However, they aren't enough to ensure the quality of a codebase as they don't test the app's interactions with the outside world, and this is where integration tests come in.
- The middle tier of an automation pyramid is integration tests. These are tests designed to verify external dependency in software applications. This can be integration with a Database, with a Framework, with third-party external software systems, or even with inter-software components between different layers. Integration tests should not be run as often as unit tests.
- At the top of the pyramid is the end-to-end tests. These tests are what they sound like: check that your app is working from start to finish and ensure that the entire system functions as expected. End-to-end tests verify the frontend integration with the backend—this level of test pyramid from the end-users perspective and how they would interact with the app.
HOW TO REDUCE PROJECT EXPENSES WITH UNIT TESTS?
To show you how unit testing looks like in practice, let’s take SCRUM methodology for two reasons:
- It has iterations;
- It operates in story points.
Story points are units of measure. Each story is assigned to estimate the total effort involved in bringing a feature or functionality to life.
Iteration is just a fixed amount of time used by the software development team to deliver the functionality.
PRODUCT DEVELOPMENT WITHOUT UNIT TESTING
As the first example, let’s take a standard iteration for the team that doesn’t write unit tests. We’ll use 12 story points as the delivery goal for the team just for the ease of calculations in the future. We’ll consider the 1 hour the developer spends on writing her code, and a QA engineer spends 1-hour testing this code.
If developers don’t write unit tests, they need about 10-20% of their time to design the features they will build. They spend 50-60% of their time on actual software development and about 20-40% fixing bugs.
QA’s time is distributed a bit differently:
They spend 50-60% to design test cases. They also spend 30-35% of their time on supporting developers to test half-made features. And they 10-15% on checking previously designed test cases to make sure that everything runs smoothly.
This 10 - 15 % has to be transferred to the next iterations to ensure that the developers didn’t ruin anything in the code.
If we take 10% of the QA time and add it to all the future iterations, the QA team won’t do anything but regression testing after ten iterations.
PRODUCT DEVELOPMENT WITH UNIT TESTING
At first, it will be difficult for your product team to start doing unit testing. You may expect that their performance will decrease, and they may deliver less than with unit tests. We can see them delivering six-story points during the first sprints. Over time, your developers will improve their skills of writing unit tests, and they will start delivering more until they reach the plateau.
From the developers' perspective:
The developers will spend almost 20% of their time designing the code. Since we're doing fewer features, we also spend less time coding them. So the developers spend around 30-40% on writing code. And also, they spend about 20-30% writing the unit tests. As a result, since we're doing fewer features and more testing, we spend only 10-20% on bug fixing.
In the first case, where the team doesn't write unit tests, they will start suffering from regression issues, and their velocity will decrease.
While in the case of unit tests, the team's velocity will continue to be the same after reaching the plateau. My point is that the more time you invest in unit testing, eventually you will benefit from it in the long run.
BENEFITS OF UNIT TESTING
Unit testing is an essential part of the agile software development process. Unit testing allows developers to check the performance of each unit and avoid defects in advance. Unit tests save your time and costs throughout the digital product development lifecycle and ensure better code quality.
Save development time
Unit tests can be set to run either a one-time check at a specific time or instantly in real-time to review changes. Thus, unit tests help developers identify bugs immediately at the software construction stage and prevent the transition of these bugs to the following stages, including after the product launch. With fewer resources and time spent on finding bugs, teams can save on fixing the bugs later in the development lifecycle. This will also bring value to end-users as they won't deal with a buggy, poorly performing product.
Contribute to code quality
Unit testing inherently increases code quality as it forces developers to write their code more efficiently and thoroughly. Unit testing also accelerates the process of finding bugs. If any changes in the code break the system, developers instantly find the cause and come up with a solution instead of running through the codebase to identify the issue.
Related article: How to Identify and Manage Technical Debt?
Provide quick access to code documentation
Nobody likes writing documentation. Unit testing provides documentation of the system that allows developers to learn what a unit provides functionality and how to use it. But if there's a new developer on the team, she can look at the unit tests and get a general understanding of the built software architecture.
Also, when we write technical documentation after we have developed part of the software, by the time we have all the documentation ready, the product will no longer be relevant since it changes all the time. Technical documentation should be dynamic. Therefore, when unit tests generate technical documentation, consider it a bonus since there is always a description of the work done in real-time.
Reduce costs in the long run
Using good unit testing tools means you reduce the overall cost of a project. Detecting an error in a unit significantly costs you less than detecting and fixing the error in the later stages of software testing.
Test-driven development has many advantages. We focused only on those which can be easily measured and compared. Of course, each project is different and has its requirements and specifications.
But in any case, the cost of writing unit tests pays off in the future because the price of changes that need to be done later without automated test coverage is much higher.