Reliable coding and unit testing

Test Friendly Coding

A few months back, I had started a project in python. I was very eager and enjoyed coding a lot. Without any future consideration, I just added lines and lines of codes with every new feature and stuff. Before I knew it, I had already written thousands of lines of codes. It was going well and all, then it started to become messed up and difficult to handle. Bugs started to show up and I had no idea what was causing it. 

Then I started unit testing hoping it might help me solve the problems but then I realized how inefficient my code was. The code was not modular, functions didn’t have specific jobs, values were changing here and there, there were lots of side effects and use of shared states. I had to restructure my entire project just to perform tests.

Now, Test Driven Development (TDD) or  Write Test After (WTA) might have made it easier but it is tedious and sometimes quite boring. Sometimes, you just want to write codes and not implement tests every single time. However, ultimately you need to perform tests so it is better to perform tests before or after each piece of code. Even though, if you are not feeling like testing and want to perform it at a later stage following some test friendly techniques might help: 

Modular programming 

Modular programming incorporates the technique of segregating a program into modules such that each module has a specific responsibility and is independent of other functionalities. It encourages the use of classes and functions for every functionality owing to more legibility, re-usability, and easy to detect errors. 

It makes testing a lot easier since we will be writing tests for particular functionalities considering each module. Also, it helps to maintain a well-managed code environment such that team members can collaborate independently on the same project without any hindrance to other coders.

Single Responsibility Principle (SRP)

Single Responsibility Principle is the ‘S’ in “SOLID” which defines the five design principles to make software designs more flexible and understandable in Object-Oriented Programming. 

This principle helps out to not only make testing easier but to maintain the entire code. It means that a class should have only one responsibility or in other words, only one reason to change. It is not only restricted to classes, functions, and modules must also carry a single responsibility. It is thus easier to test functions that are performing only one basic operation.

Using Pure Functions

As the name suggests, a pure function is pure in the meaning that it only operates on the input it is given, nothing else, and always returns an output just based on given inputs, and thus it should not have any side effects. 

This helps in testing a lot since we are sure that it does not alter any other values above its jurisdiction, making them highly predictable and we only have to assert the output of the function during tests. The tests will be simplified to a single function without the need to account for other effects of other functions.

Avoid the Shared States

Shared State is a state which is shared between multiple functions or programming modules. It can be any variable, object, or memory space existing between multiple scopes. The most common example is a global variable. These shared states can cause a lot of trouble during programming, let alone testing.

 When considering a function that uses a shared state, we will need to consider the entire history of the state, so the functionality of the question is under question and dependent on the shared state. I had the same issue and had to track a global variable all the way just to determine the output of the function to be tested. 

It also results in other problems like difficulty in understanding code, confusion between states under the same name, can be easily manipulated so it results in code vulnerability. So, the use of shared states is highly discouraged.

Prefer immutability over mutability

An immutable object is that which cannot be modified once created. On the other hand, a mutable object can be subjected to multiple changes during the program cycle. Thus, a thorough test needs to be done to check the state transitions, henceforth for the dependent functions. In contrast, when a function is passed immutable parameters, we are certain the parameter cannot be changed, so it is consistent and predictable. 

If we reference the same parameter again at some point in the program it will keep its value intact. So, we will be freed from the burden to track it in the program since its value is the same everywhere. Immutability sometimes seems to be confusing. Consider a function in javascript and we pass an array as an argument and push additional value to it. 

var a= [3, 4, 5];

function hello() {

    a.push(6);

…do something

}

When doing this, it mutates the original array passed into it. So the value of ‘a’ after the function call becomes [3,4,5,6]. We can also do it immutably(ES2015) as:

const a= [3, 4, 5];

function hello(a) {

let new_a= […a]

    new_a.push(6);

…do something

}

In this case the value of ‘a’ remains [3,4,5], so the array does not change and it is consistent, hence easier to test.

Setting Aside Side Effects 

A side effect is set to occur if a program module alters some state variables outside its local environment and the effect can be observed in other states except the returning value. Some examples of side effects can be considered as modifying an external variable like a global variable, writing to files, databases screen, console, network requests, etc. Isolating these side effects form the main code makes testing a lot easier.

Most of the above-mentioned points are contained in the foundation of functional programming, thus it would not be wrong to say follow functional programming for test-oriented coding. 

Functional code tends to be more legible, predictable, consistent, easier to test, and easier to change without violating tests. This concept applies to the majority of programming languages including javascript, java, python, PHP, Ruby, and so on. So, these practices will not only help you in performing unit tests but also make your code robust, manageable, understandable, and error-free.

Recent Posts

Description of Author

  • Sajan Amatya is an Electronics and Communication graduate from Institute of Engineering, Tribhuvan University, Nepal. He is a Frontend developer by profession with experience in react and react native. Other than that he is interested in drones and robotics in which he has had some experience working with Prokura Innovations. When he is free from work, he finds himself watching movies, listening to music and watching/playing football. He is a big fan of football and a follower of Chelsea FC.

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *