09 January 2018

Unit Tests - Are they important?

While I was going through some of the projects in GitHub, I found that almost all the projects have automated unit tests (I hope you will also see many of your projects have that. These days it is quite common to start off by writing tests first). These tests are run most of the times - before every commit, after every commit, nightly tests and at various stages. These tests are a critical and important piece in CI/CD. Tests, in general, are the cornerstone to software development. The tests are proof that the specific part of the software is working correctly and consistently.  In this post, I am going to talk about unit testing and in subsequent posts show some examples (in Python but you can find similar tools in other modern languages)

What is testing?
It is the systematic exploration of the subject and verifying all the aspects of the subject is intact. It is the philosophy that a faulty brick is responsible for bringing down a massive building. The main purpose of testing is building quality (correctness and consistency) and the side effect is "defects". 
What is a test case?
A test case is a scenario which ensures that a certain aspect of the subject works as intended (or does not work which becomes a defect). When you change the condition, it is altogether a different scenario. A test case with the correct result is the proof that the system works fine in a specific scenario. A test case is narrow (in the sense that it verifies only a specific part/function), to the point and repeatable. It is concrete, it improves confidence and it builds quality.

Top-down approach
When you test a bigger piece, it is often challenging to find out the boundary of the piece. As you explore, you tend to get a feeling that the bigger piece is expanding as you explore (and often never-ending as you come to know more about the system). It quickly gets out of control. The top-down approach (knowing the bigger system first and then decomposing it further, keep working on it until you come to a unit) is time-consuming and tedious to do in all situations (someone fixing a small bug needs to communicate why she has fixed that brick), and delaying the validation of a unit until bigger piece is built is ineffective.  The top-down approach should focus on the problem domain and wider aspects/functions much at a higher level.

Bottom-up Approach
This is not to say that top-down approach is wrong. Rather the top down is not sufficient or it is sufficient but fixing it when the system is developed fully is going to hurt the product in many ways (very likely). It needs to be complemented by the bottom-up approach. Write your "unit of code" and while you are writing your "unit of code", write your own tests. It is even better if someone writes unit test code for your unit of code. The top-down and bottom-up are two different perspectives of the same system (the first perspective is system has components and the second perspective is the components make the system)

What is a unit of code and unit testing?
This is a very vague term, often confusing and means different things to different people. We can consider a unit of code as "smallest possible amount of code that can be tested". You can call a function as the unit (like the ones that we are going to see tomorrow) but then the entire main() function where your application runs is too big to be called the unit of code. So, apply your mind when deciding how smaller or bigger is the unit of code.

A unit test is a scenario (setup, input, output, validation, cleanup, and reporting). Each unit test will have certain environment or conditions that are assumed to be present, input values, output returned, the validation that proves that the test is passed or failed, cleanup and finally last but not least reporting the success or failure.

When the unit test suites/cases can be run?
It is an absolute must that a developer writes unit tests before she writes production code. It is the test code that is to be written first and runs or at least while you are writing your code (writing test code after you commit your code is the biggest sin that you can do to your code). The idea is to break the software and keep fixing it. With that philosophy, it is a critical piece in software development. The unit tests have to be run successfully during the development stage (numerous stages and numerous times), before the code is committed, after the commit and nightly builds.

Why automated unit tests?
If you look at the above point, it stresses the importance of running them all the times. So, it becomes easier (and productive too) to write/automate once and run millions of times throughout the life of the product. So prefer automated unit tests over manual.

In the above analogy, unit tests are certainly examining each and every brick that they are good. The other forms are testing ensures that the joints between bricks are correct, the walls are correct, all the room comply with the requirements and overall the building is a masterpiece.

In the next post, we will start off with a simple example, learn the trades of unit testing and learn Python's built-in "unittest" module.


No comments: