In software engineering, unit testing is a test (often automated) that validates that individual units of source code are working properly. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual program, function, procedure, etc., while in object-oriented programming, the smallest unit is a method, which may belong to a base / super class, abstract class or derived / child class.
Ideally, each test case is independent from the others; Double objects like stubs, mock or fake objects as well as test harnesses can be used to assist testing a module in isolation. Unit testing is typically done by software developers to ensure that the code they have written meets software requirements and behaves as the developer intended.
The goal of unit testing is to isolate each part of the program and show that the individual parts are correct. A unit test provides a strict, written contract that the piece of code must satisfy. As a result, it affords several benefits.
Following three steps of unit-testing effectively address the goal of finding faults in software modules
1) Examination of the code:
The code is examined thoroughly throughstatic testing methods like Reviews, Walkthroughs and Inspections etc.
2) Proving the correctness of the code:
- After completion of the exercise if coding and review etc. we would like to confirm the correctness of the code. A program is said to be correct if it implements the functions and data properly as indicated in the design and if it interfaces properly with all other components. One way to investigate program correctness is to view the code as a statement of logical flow. Using mathematical logic, if we can formulate the program as a set of assertions and theorems, we can show that the truth of the theorems implies the correctness of the code.
- With this approach we become more strict, rigorous and precise in our specification. This would require great amount of effort in setting up and carrying out the proof.
3) Testing of Program components or Units or Modules:
- In the absence of simpler methods and automated tools, “Proving code correctness” will be an elusive goal for software engineers. Proving views programs in terms of classes of data and conditions and the proof may not involve execution of the code. On the contrary, testing is a series of experiments to observe the behavior of the program for various input conditions. While proof tells us how a program will work in a hypothetical environment described by the design and requirements, testing gives us information about how a program works in its actual operating environment.
- To test a component, input data and conditions are chosen to demonstrate an observable behavior of the code. A test case is a particular choice of input data to be used in testing a program. Test case are generated by using either black-box or white-box approaches
Advantages of Unit Testing:
Unit testing allows the programmer to refactor code at a later date, and make sure the module still works correctly (i.e. regression testing). The procedure is to write test cases for all functions and methods so that whenever a change causes a fault, it can be quickly identified and fixed.
Readily-available unit tests make it easy for the programmer to check whether a piece of code is still working properly. Good unit test design produces test cases that cover all paths through the unit with attention paid to loop conditions.
In continuous unit testing environments, through the inherent practice of sustained maintenance, unit tests will continue to accurately reflect the intended use of the executable and code in the face of any change.
Depending upon established development practices and unit test coverage, up-to-the-second accuracy can be maintained.
Limitations of Unit Testing:
Testing cannot be expected to catch every error in the program. The same is true for unit testing. By definition, unit testing only tests the functionality of the units themselves. Therefore, it may not catch integration errors, performance problems, or other system-wide issues. Unit testing is more effective if it is used in conjunction with other software testing activities. Like all forms of software testing, unit tests can only show the presence of errors; it cannot show the absence of errors.
Software testing is a combinatorial problem. For example, every Boolean decision statement requires at least two tests: one with an outcome of “true” and one with an outcome of “false”. As a result, for every line of code written, programmers often need 3 to 5 lines of test code.
To obtain the intended benefits from unit testing, a rigorous sense of discipline is needed throughout the software development process. It is essential to keep careful records not only of the tests that have been performed, but also of all changes that have been made to the source code of this or any other unit in the software. Use of a version control system is essential. If a later version of the unit fails a particular test that it had previously passed, the version-control software can provide a list of the source code changes (if any) that have been applied to the unit since that time.
It is also essential to implement a sustainable process for ensuring that test case failures are reviewed daily and addressed immediately. If such a process is not implemented and ingrained into the team’s workflow, the application will evolve out of sync with the unit test suite ― increasing false positives and reducing the effectiveness of the test suite.