First time when I started writing Unit Test cases in Java, it wasn't because of interest or belief but more because of agreed deliverable of the project. Since it was imposed, I didn't realize the value. I created some Unit test cases just as a post development artifact to show the work done. The test cases weren't exhaustive and obviously I wasn't looking for any benefit from them. After certain level of development, my test cases (which were not very useful to start with) became totally useless. I didn't go back to update/correct/complete them in time. I started showing this failure of the test cases as example against writing the Unit test cases with some success as well.
I am sure, I wasn't the only one at that time with this mindset. There would have been many similar people at that time and even there would be many today as well. But today I am a changed man. I do understand the benefit of writing test cases and turning to be an advocate for the same.
Most of the time we fail in achieving the desired results because of several reasons. Some of them could be mindset related as below:
- We don't believe in action being taken.
- We don't see the value especially to ourselves.
- We don't take it as our responsibility to make it successful.
Here is the list of bigger hurdles (I think of) in Unit Test success and suggested mitigation plans.
1. Lack of understanding the "Unit": Most of the time we start thinking of a whole business case as a testable unit. This leads into enormous number of complex test scenarios and multiple integration points. Having complex scenarios with uncontrolled integration points, it becomes almost impossible to write an Unit Test case.
To solve this problem, let's understand the Unit. We can imagine units as atoms which are the smallest possible particle in the coding world but self-contained (I am not saying electrons/protons or even molecules). One Unit of code should have smallest set of processing logic which takes the system from one state to another. Let's take an example of car. If we consider Car as a business case, we can consider wheels, head light, tail light, steering wheel, horn as it units. Each unit has certain specification and defined responsibility. If I need to write test case for a headlight, my some of the possible scenarios could be as below
- It turn on if input supply is on
- It turns off it input supply is off
- It shows high beams with input type as high beam
- It shows low beams with input type as low beam
Once we have clean units defined, it's become very intuitive to come up with the possible test scenarios and creating test cases against them.
2. Unit Testing and Development are two different activities: As a developer I can always argue that I am not responsible for writing Unit test case as there is a dedicated QA team. This leads into passive response from the developers in creation of Unti test cases while they are the best people to come with test cases as they understand the code best.
Unit testing should be treated as the integral part of the development. Also developers should implement the functionality with the mindset of unit testing. The source code should be as modular as possible with clear defined responsibilities for each unit. Unit testing can't be successful by carrying out Unit testing as a secondary task; carried out after the development. Instead Unit testing should be part of the development from the day one and carried out with the same developer (if possible).
3. Functionality is more important than Unit Testing: When working on a project with tight deadline, developers tend to meet the functionality ignoring the test cases. Once the functionality is implemented, momentarily they celebrate, but very soon it turns into disaster after several bugs are discovered. Fixing the bugs at last moment without having sufficient Unit test cases becomes more risky. It becomes very tough to overcome the scenario even by putting extra help and effort.
I agree that Unit testing is not an end user deliverable but at the same time its lifeline for the end user deliverable. As we won't drive a car whose breaks/gas/parts are not fully tested, we shouldn't expect the user the start using an application which is not systematically tested. We should consider Unit testing equally important as developing the functionality. I am sure; you will start realizing the benefit as soon as you start following it with full belief.
4. My input/output is varying all the time: As soon as it comes to unit testing, first argument we listen is the input data is continuously changing. My test case might work today but it may fail tomorrow just because of the data. Also sometime we listen that output is dynamic, then how to validate that.
Here I would like to emphasize one thing. Unit testing is not about how to test the unit. As a developer, you are doing it all the time to validate your implementation (if not, even more is missing). Unit testing is all about automating the test scenario at development time and later reuse it as regressive test suite. Once we have clean definition of Unit, we need to prepare our own test data to meet all our test scenarios and use that test data in Unit test cases. Output of the unit is a derived value based on the input and hence we should be able to device the expected outputs. If we are using some functions such as Random, we need externalize it and replace it with genuine inputs. This way I can say that we clearly understand the concept of unit testing, we will never talk about dynamic input/output. Also as I mentioned above, it's an activity to be performed along with development, not separately.
I hope this is helpful.