A watch works perfect if all the parts of the watch work perfect. Although if all the parts work for their task but not for the watch then you are sure there is something wrong with integration.
It is true for a software as well. We have came a long way from procedural programing to object oriented programming. We know many classes will combine to make a bigger software for end user. But when you find a bug in your end user product, you travel all the way from UI level to application level to business logic level to find a small out of bound scenario in library code. And you find many more such bug after integration. Why couldn't you caught it earlier? Well you couldn't test it until it was integrated. Lets say you solved all the bugs found after integration by hunting them down one by one, can you still be sure there wont be more bugs? No, you can't, because you might not have executed each line of the library level code while system testing. To say confidently, you have no bugs in your code you should have each line of your code checked. Also no tester has to know about the code, so the developer is the best person to make sure that each line of each program is safe and bug free. Yes we call it Unit Testing, supposed to be done by developer while implementation.
Of course we do check our code by debugging whenever we write a new program, by stepping on each line and checking value of each variable. Or do we? Lets accept the truth, debugging each line is tiresome and boring. Nobody wants to do boring job. Actually we can out source the boring job to Automated Unit Tests. It is easy to write automated unit tests without using any third party tool, but it can just be implemented in the same programming language you are using.
The Unit testing can be defined as the testing of each unit independently. Unit is the smallest testable entity in the system. In object oriented programing the unit can be a class or a function of a class.
Automated Unit Tests can be written in the form of the programs that will check the status of the class before and after execution of a single function. Generally the checks are in the form of preconditions and post-conditions for each functions. Precondition will check if everything that is needed to execute the function is ready and post-conditions will make sure the expected changes has been done in the class. We write such unit tests for each function, we write it while development itself. We write two functions in a pair; one is to achieve the functionality and other to test the implemented functionality. This might seem to be an extra work, but it is not, as this will save a lot of your time by detecting almost all bugs immediately after implementation of the function, because now you can test the function immediately you have implemented it. Also once you have your infrastructure ready for unit tests, this will become very easy and fast.
A proper infrastructure is needed to keep unit tests separated from your deliverable code. The new developer should not confuse unit tests as functional programs, hence the well defined nomenclature is necessary for the unit test programs. To achieve the purpose you implement separate unit test class for each new class in your system. For example if you implement a class called Circle, implement another unit test class called Test_Circle;
| Functional Class | Unit Test class |
| class Circle { public: Circle(const double& radius); double CalculateArea() const; double CalculatePerimeter() const; protected: double m_radius; private: } | class Test_Circle { public: Test_Circle(); void Test_CalculateArea() const; void Test_CalculatePerimeter() const; } |
Notice that test class has corresponding test functions for each of the function in Circle class. While checking the pre-condition and post-condition needs to access the private/protected members. We can achieve the behavior by making test class a friend of the actual functional class. However I prefer to inherit the test class with public keyword from the functional class. For the Circle class member variables to be available in test class I have to keep those protected in Circle class.
Class Test_circle : public Circle
{
}
There is a another reason I prefer inheritance over friendship of the functional and unit test class. If I have a class having several functions implemented and a single function that is pure virtual. It makes my functional class abstract and I will not be able to make an instance of it until I inherit the class and implement the pure virtual function. Although abstract classes has to be implemented eventually, I do not want to wait for it's implementation. It is lot easy for me to just inherit unit test class and implement the pure virtual function with dummy code (or keep it empty) and test other functions.
| Functional Class | Unit Test class |
| class Shape { public: double CalculateArea() const = 0; double CalculatePerimeter() const = 0; bool IsValid() const { bool valid = false; if (0.0 < m_radius) { valid = true; } return valid; } protected: double m_radius; private: } | class Test_Shape : public Shape { public: Test_Circle(); void CalculateArea() const; { //dummy code return 0.0; } void CalculatePerimeter() const; { //dummy code return 0.0; } void Test_IsValid() { // Modify radius to some invalid value m_radius = 0.0; bool check = IsValid(); // Above statement should return false, // check it by asserting assert( !check ); // Modify radius to some valid value m_radius = 1.6; bool check = IsValid(); // Above statement should return true, // check it by asserting assert( check ); } } |
Now you can test even the abstract class Shape without waiting for creation of its subclass.
Although I have used very simple functions in my example, writing unit tests are much useful testing complex computations. Your system is testable since the first day of your implementation.
We can have a single function in each unit test class that calls all the test functions. We can keep all the unit tests files in a single folder and implement a system that goes into the folder and creates instances of the unit test class and calls the main functions that call all the test functions. You can run the test as a routine tests to check any regressions due to modifications. If you get any failures, you will get those in a single functions, and if you have written very smaller function (which you should ideally do) you will find the solution in minutes.
Unit tests in a place makes a developer independent of the abstraction layer above their code. Hence each lower layer can be implemented without knowledge of the details of it's upper layer. This makes your code more modular and loosely coupled.
Unit tests can be made intelligent to detect any dependency violations. Lets consider you have two modules in your project libraries and application. Application module is dependent on library module. You have unit test implemented for library classes. The unit test are called by some exe that is linked using all the classes available in library. If some developer has included a header file from application module in library classes. The code will not link, as it only has library files for linking.
One of the best usage of Automated Unit Tests can be done if you are following Test Driven Development (TDD). TDD is the approach where you write the the test cases first and let it fail. Then you work towards the implementation to make the test pass. It's a evolutionary concept and needs a totally different mindset to follow. But now you have the practical method to go for TDD.
Automated Unit Tests has many advantages which can be realized once you start writing some by yourself. The confidence in my code with Automated Unit Tests in place, motivated me to share what I have experienced. I hope this will be helpful for you.
Any objections, rejections or suggestions are welcomed, as every new comment will either clear my misconception or will teach me something new.