This post is slightly different then the usual post on the site. It is not so much a tutorial as it is a form of accountability. With the tekst below you should have all the ammunition you need to convince you manager or product owner of the need for unit testing.
Traditional testing tests the application as a whole. While this is very important, there are some drawbacks:
- Individual units are rarely tested, since some units are there for edge cases only. They can be difficult to test when testing the application as a whole.
- Errors go undetected, because some parts of the application are hard to reach when testing the application as a whole, errors in those parts are difficult to detect.
- Isolation of errors is difficult. When an error is detected, it is hard to determine exactly what caused the error.
Traditional testing postpones the validation of the system to after the development phase. Due to a number of reasons, the system may not operate correctly:
- Poor interface/API design
- Procedures/functions/methods not written correctly
- Integration not correctly performed
- Requirements not clearly defined
In computer programming, unit testing is 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.
Testing components individually has some benefits.
- Each part tested individually
- All components tested at least once
- Errors picked up earlier
- Scope is smaller, easier to fix errors
Unit testing aims at testing each of the components that a system is built upon. As long as each component works as defined, then the system as a whole has a better chance of working.
Unit testing vs. Traditional testing
Unit testing is a good partner for traditional testing. When using unit tests, traditional testing is more about testing the integration between the components and not testing the working of the individual components. This takes care of the drawback in traditional testing.
- All individual units are tested with unit tests
- Since all individual units are tested with unit tests, errors rarely go unnoticed.
- Isolation of errors is easier to track, since traditional testing is only needed for integration between components.
Aside from handling these drawback, the other concerns with traditional testing are also addressed properly.
- During the writing of unit tests, the interface and API design is indirectly verified.
- Unit tests are there to test the individual methods.
- Since the individual components are now handled by the unit tests, the traditional test can focus on one task, integration testing.
- During the writing o the unit tests, any parts of the requirements that aren’t clear usually come up early. This means they can be cleared up early as well.
Why not Unit Test?
A lot of teams either struggle to write unit tests or simply don’t write them. There are many reasons for this the most common ones are stated below.
- Legacy code: when working on an older codebase without unit tests, adding unit tests can be a major endeavor.
- Untestable code: code quality can be so low that unit testing is next to impossible.
- Project Management: traditional project management tends to focus on delivering new functionalities as fast as possible.
Sometimes changes need to be made to an older codebase. When this codebase does not have any unit tests, adding them becomes a big endeavor. Usually, this is seen as cost ineffective. Why spend a lot of time on an old codebase, when all that is needed is a simple code change?
Low code quality
Sometimes the code quality is so low that the code effectively becomes untestable. When this happens, the most common solution is not to write any tests and rely on traditional testing. The solution however should be to refactor the code to make it testable, and add the unit tests. The reason this is not done is that doing it this way often takes a lot of time and effort without any real business benefit.
When working on a project, the main focus is usually on delivering business value. Traditional project management tends to think of business value as new features. This means that the development team is under constant pressure to deliver the next feature. Often this means that unit testing is skipped, since it takes more time while not directly adding new features.
Why unit test?
Unit testing is done for various reasons. The most important ones are listed here.
- Find problems early
- Facilitate change
- Simplify integration testing
- Supports design (Test Driven Development)
Find problems early
When there are good unit tests, it becomes easy to detect flaws in the code logic. A unit test will setup a verification that input A leads to result B. When a flaw in the logic turns the result into C, this is detected at build time, on the computer of the developer that created the logic flaw. Detecting flaws as early
as possible means they are fixed early as well. Detecting and fixing problems early is cheaper than detecting and fixing them later. It also means that the
integration testers can focus on just the integration testing.
When there are good unit test, changing code is far lest dangerous. Often, it is hard to predict if changing the code in one location has unexpected
consequences in a different area. By having good unit tests, these kinds of problems are easier to detect. Unit tests do this by verifying that a method
keeps giving the same results on the same inputs, even when the underlying logic changes, This is especially useful when refactoring existing code.
Simplify integration testing
Because unit tests take care of testing the logic inside a single unit of work, the integration testing just has to verify that all units are working together
nicely. This means there are a lot less test cases to go through during integration testing, because the unit tests already verified that several test scenarios give the same result when they go through a unit. For instance, an empty string and a null string give the same result, so only one needs to be tested during integration testing.
When using test driven development, or TDD, the unit test can also be used create the skeleton of the application. By writing the unit tests first it is
possible to detect if the design is actually possible, and to give an estimate as to how difficult it will be to implement. The test cases written in this case are bare test cases since the units being tested are still completely empty. After setting up the skeleton of the application in this way, writing the application becomes simpler, since the unit tests are already in place. Each line of code written is aimed ad passing the unit tests.
This form of programming is difficult to start with since it’s a big change from the normal way of working. Once the transition has been made, the benefits are great though.
Because of some, if not all, of the above reasons, unit testing is at the core of agile development.
Because of the speed of agile development, it is ever more important to deliver robust code. This means detecting flaws as early as possible is key. This is also one of the tenants of the agile manifesto, Fail early, fail often. Unit testing can help with that.
Change is also a given when working in an agile manner. Since the user stories often come back to the same code, code gets rewritten a lot. This means there has to be a way to verify that those changes don’t break anything else unexpected.
Because of the many changes inherent to the agile way of working, the integration testers are busy enough with testing the integration of all the various parts of the application. If unit testing is in place, this takes work away from the integration testers meaning they can focus more on the actual integration tests.
By adopting Test Driven Development it becomes possible to design a new component, or changes to an existing component, first and write the
implementation after. Since this is the first task performed after taking up a user-story, it is possible to test the estimate early. If there are a lot more unit tests than anticipated during refinement, changes are good that the estimate is too low.
As outlined above, working in the agile way without implementing decent unit tests is not advisable. Having unit tests results in code that is more stable, better maintainable and more often than not more readable. All of which are key to make the agile way of working a success. Unit tests also result in a form of documentation since they explain how the code should react in any situation. This means less actual documentation has to be written.
Traditional project management focuses on features. Because of the reasons stated above, it is important to get project management on board. Unit testing might seem a time consuming effort in the short term, but the long term benefits outweigh that initial costs. It is the job of the development team to make project management understand the benefits of unit testing.
This tutorial continuous in the next part of the series, Unit Testing, JUnit