Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation. The primary goal of unit testing is to take the smallest piece of testable software in the application, isolate it from the remainder of the code, and determine whether it behaves exactly as expected. Each unit is tested separately before integrating them into modules to test the interfaces between modules.By means of effective Unit testing large percentage of defects are identified. Unit testing is performed by developers.
Each module that is developed by designers need to be tested individually to verify proper operation so that any faulty module can be fixed immediately rather than let it exist and then cause some major issue in the integration phase. Once all of the units in a program have been found to be working efficiently and without any bugs, larger components of the program can be evaluated by means of integration testing. Though Unit testing may be time consuming, tedious and requires thoroughness on the part of the development team, but in the long run it can avoid major pitfalls in the software.
Benefits of unit testing:
- The modular approach during Unit testing eliminates the dependency on other modules during testing.
- We can test parts of a project with out waiting for the other parts to be available.
- Designers can identify and fix problem immediately, as the modules are best known to them. This helps in fixing multiple problems simultaneously.
- Cost of fixing a defect identified during the early stages is less compared to that during later stage.
- Debugging is simplified.
- Structural coverage of code is higher.
- Unit testing is more cost effective compared to the other stages of testing
Misconceptions about Unit Testing:
- Integration Tests will Catch all the Bugs Anyway: This is one of the common misconceptions of designers. Complexity of the issue rises while it passes through various testing cycles and then when the bug is raised during the later stages, the resolution time will be high as the scope of the bug widens. It is better to weed off the crop before it poisons the whole farm.
- Programmers Dilemma: Most designers believe Unit testing is not actually required as it is time consuming. They feel they are too good programmers and their software doesn’t need Unit tests. But in the real world, everyone makes mistakes. Real software systems are much more complex. Software behavior various in different environment and with different scenarios. Coding is not a one pass process. Enhancements to the code can be made only when we know the existing module is functioning as expected.
- It Consumes Too Much Time: Developers are often in a hurry to complete their code and integrate it. Unit Testing is most often considered a useless activity, as they feel anyways the code will be tested by QA. There is no point in having a system which works but not exactly as it is supposed to function and is to be full of bugs. Practically, such an approach to development will often result in software which will not even run. The net result is that a lot of time will be spent tracking down relatively simple bugs which are wholly contained within particular units. Individually, such bugs may be trivial, but collectively they result in an excessive period of time integrating the software to produce a system which is unlikely to be reliable. This can also lead to failure to meet the required deadlines.
Unit Testing Best Practices:
- Ensure each Unit Test case is independent of each other. As the software is prone to changes during the Unit Testing due to enhancements/changes to the requirements. Hence any given behavior should be specified in one and only one test. Otherwise if you later change that behavior, you’ll have to change multiple tests.
- Test only one code at a time. It is always recommended to test each of the modules independently and not while all are chained together. Otherwise you will have lots of overlap between tests and changes to one unit may effect all other modules and cause the software to fail.
- Name your unit tests clearly and consistently. Ensure that your test cases are easily readable so that anyone picking up Unit test cases can execute them without any issues. Ensure the test case nomenclature is consistent throughout.
- Before changing a module interface or implementation, make sure that the module has test cases and that it passes its tests before changing the implementation. This way you can know that your changes didn’t break anything.
- Always ensure the bug identified during Unit Testing is fixed before moving it to the next phase.
Unit Testing Techniques: Structural, Functional & Error based Techniques
Structural Techniques: It is a White box testing technique that uses an internal perspective of the system to design test cases based on internal structure. It requires programming skills to identify all paths through the software. The tester chooses test case inputs to exercise paths through the code and determines the appropriate outputs. Major Structural techniques are:
- Statement Testing: A test strategy in which each statement of a program is executed at least once.
- Branch Testing: Testing in which all branches in the program source code are tested at least once.
- Path Testing: Testing in which all paths in the program source code are tested at least once.
- Condition Testing: Condition testing allows the programmer to determine the path through a program by selectively executing code based on the comparison of a value
- Expression Testing: Testing in which the application is tested for different values of Regular Expression.
Functional testing techniques: These are Black box testing techniques which tests the functionality of the application. Some functionality testing techniques are:
- Input domain testing: This testing technique concentrates on size and type of every input object in terms of boundary value analysis and Equivalence class.
- Boundary Value: Boundary value analysis is a software testing design technique in which tests are designed to include representatives of boundary values.
- Syntax checking: This is a technique which is used to check the Syntax of the application.
- Equivalence Partitioning: This is a software testing technique that divides the input data of a software unit into partition of data from which test cases can be derived
Error based Techniques: The best person to know the defects in his code is the person who has designed it.
Few of the Error based techniques are:
- Fault seeding techniques can be used so that known defects can be put into the code and tested until they are all found.
- Mutation Testing: This is done by mutating certain statements in your source code and checking if your test code is able to find the errors. Mutation testing is very expensive to run, especially on very large applications.
- Historical Test data: This technique calculates the priority of each test case using historical information from the previous executions of the test case.
Careful approach to Unit testing helps detecting many bugs at a stage of the software development where they can be corrected economically. It is a tedious process when bugs are detected and corrected at later stages of software development as fixing the bugs is difficult, time consuming and costly. Efficiency and quality are best served by testing software as early in the life cycle. Whenever any changes are made to the software we need to ensure regression testing is performed. Testing strategies like thorough unit testing, good management of the testing process, and appropriate use of tools helps in maximizing the effectiveness of testing effort. Effective unit testing is all part of developing a very high quality software product which can benefit the organization on a whole.