Unit Testing is all about proving something you code is working correctly. It’s an important tool in the testing process. Whereas integration and usability testing are more customer-centric (verifying high-level requirements), unit testing is the first line of defense for a programmer. It will not directly improve the quality of the application and is not guaranteed to reduce the development and testing time or get your application to the market faster. It’s all about proving correctness. The Unit Testing of well-written code will provide a measurable degree of confidence that the methods written will behave correctly.
The benefits of unit testing are:
- Provides objective information that can be fed back into the development process to improvise it.
- Repeatability or regression testing. This helps to reduce the time to verify that the application behaves correctly after a minor or significant code change.
In unit testing, let’s look at the ideal methods to write a unit test and the correctness of the unit.
A pure unit should have the following:
- It should not call other methods.
- It should do only one thing.
- It should not have multiple code paths.
For the correctness of a unit, we should be focusing on the following:
- Testing how the unit behaves under normal conditions.
- Testing how the unit behaves under abnormal conditions.
To prove correctness:
- Verify that the inputs to a computation are correct.
- Verify that methods result in the desired computational result.
- External error handling and recovery.
Unit testing is an iterative process, there will always be bugs that are missed with unit testing. There is no measurement to say "With unit testing, the number of bugs have been reduced by x% percent". Let’s look at the simplest way to measure it that can be used as a benchmark for the correctness of the application.
- Test Coverage.
- Number of bugs reported over time and number of unresolved vs resolved issues.
- Number of unit test cases written for reported bugs vs. total number of test cases.
- RCA analysis to identify number of potential SIT bugs should have been part of unit bugs
Strategy to implement
Approaches depend on where you are in the project and your budget in terms of time, money, manpower, need etc. Ideally, unit testing is budgeted into the development process, but realistically we often encounter existing or legacy programs that have little or no code coverage but must be upgraded or maintained. There is a need to create a reasonable unit testing strategy and it should provide measurable benefits to the project to offset the liability of their development, maintenance and their own testing. The strategy that we adapt for unit testing can affect the architecture of the application. Model-View-Controller that facilitates unit testing is typically easier to unit test. Whereas this is nearly always a good thing, it may introduce unnecessary overhead for your needs.
Unit testing can be a benefit even if we are involved in the maintenance of an application, one that requires adding new features or simply fixing bugs. But planning and investigation into the code may require more resources.
Approaches that can be taken are:
- Target writing unit tests only for new features and new bug fixes.
- Target for existing features.
- Refactor code to isolate code units.
Process that can be followed to implement unit testing are the following:
- Test Driven Development ( TDD)
It facilitates a more formalized development process by the discovery of actual testable units and isolating them from boundary crossing dependencies.
Advantages:
- Instills the discipline of unit testing and writing the unit tests first.
- Easier to determine developer following the process.
- Enforces modular architecture.
- Code First, Test Second
Coding first is more natural because that’s the usual way applications are developed. This approach requires a closer monitoring of the team.
- Test using some other way
Just because you don’t have unit tests doesn’t mean that you are throwing out testing. It may simply be that the testing emphasizes acceptance testing or integration testing.
A cost-effective unit testing process requires a balance among all the three processes above and factors such as the experience of the developers on the team. As a manager, we should not decide on a test driven approach if the application is healthy and need to instill discipline and approach.
In a nutshell, unit testing doesn’t replace other testing practices and it’s a good practice to follow. This requires a disciplined approach, commitment to time and effort required to implement and maintain tests. From a coder’s perspective, it requires good programming practices and enforces architectural decisions. Thus reducing costs and improving maintainability not because code is unit testing but because code is written better so that it can be unit tested.