Clean Code Matters: Unit Testing [Part 2]

Posted by

You can read Unit Testing Part 1 here.

Unit Testing Examples 

Following are a few example code snippets to understand what unit testing is and how to start writing them. These examples are all written in Java using the JUnit unit testing framework.

In the code snippet above, we are adding two numbers and returning the sum. This is a pretty simple example to start with. And this simple method alone can have multiple test cases.

As you can see from the code snippet above, there are multiple test cases for the same method that does just one job, adding two numbers. Because of the simplicity in this example, we don’t even need to mock anything for testing this.  

In all these unit tests, we are asserting that the sum returned by the method is equal to the expected sum of the numbers that we’re passing to the method. And all these unit tests pass as the expected result matches the actual. It is difficult with this example to show a failing unit test where the output of the method is wrong. So instead of that, I’m going to change the expectation in the last unit test to 4 instead of –4.

As you can see, this will fail the test because the expected and the actual values don’t match. 

Let’s look at another example where we divide a number from another. In division, we encounter an exception when we try to divide a number by zero. So, in this method, we will have to check if the second number is a zero, for which we will use a method in another class. The code looks like this: For testing this, we don’t want to test the checkNumberIsZero() method, as it is a separate method and is in a separate class. We test this individually. So, we just mock this method. In Java, we can use the @Mock annotation to mock this class and all the methods in it.

Now, in our unit tests, we can control the output of this method so that we can test various scenarios. In the first unit test, we can see that no matter what double we pass to the method, we’ll get the output as False. This is controlled using the when().thenReturn() method calls. This way, we are mocking the checkNumberIsZero() method and testing only the divideNumbers() method.  In the second method, we are passing zero as the second number, and we are mocking the checkNumberIsZero() method to return the value True, as expected. And as you can see, we are asserting if the divideNumbers() method throws an exception or not. And as expected, it does and the test passes. 

In the third unit test, we’re doing the same with the mock, but instead of passing a zero as the second number, we are passing two. In this case, we are expecting the result to be two, but because we’re returning a True from the checkNumberIsZero() method, the divideNumbers() method throws an exception instead of returning two. This, as you can expect, fails the unit test. 

This way, we can mock all external method calls from the method we are currently unit testing to be certain that the method is doing what it is expected to do. This way, even when the method is extended with more functionality, all we have to do is write more unit tests to cover the new lines of code, and run the existing ones as well and make sure everything is green. This makes refactoring and extending the functionality of a method way easier compared to not having unit tests. 

Advantages Of Unit Testing 

As you can imagine by now, unit testing has a plethora of advantages. In this section, we are going to look at a few of them. 

  • Makes extending and refactoring of code easier: Because unit tests help in determining if a method is working as expected or not, it becomes easier to both refactor that method or extend the functionality of that method without much worry. Once we add more functionality or refactor the code, all we have to do is run the unit tests again to check if they are all passing or not. If every test is green, we are good to release the changes. 
  • Improves quality of code: If followed properly, unit testing can help improve the quality of code tremendously. This is because when unit tests are integrated into the build and deploy pipelines, we can’t release any code till we make sure that all unit tests are passing. This by default makes developers write good quality code, including good design, breaking down complex methods into smaller chunks, reusing existing methods, etc. 
  • Helps in finding bugs easily: Unit testing is the easiest way of finding out and squashing bugs in the code. Because we set the expectation from the code clearly in the unit tests, any deviation from that expectation is caught immediately, before even releasing the code. This way, any bug in the code is fixed during the development phase itself. 
  • Makes debugging easy: Because unit tests tell exactly which test is failing, we can easily isolate the problem and fix only that part of the code. We don’t have to spend a lot of time hunting for the cause of the bug or go through a lot of code to figure out the issue. 
  • Helps in improving the design: By definition, unit tests are designed to test the smallest possible piece of code. This means we first have to split the code into small independent code blocks. This implicitly improves the design and promotes reusing existing code. This even improves the use of object-oriented concepts in OOP-based programming languages. 
  • Helps in reducing cost to the organization: A big part of a software project could easily become debugging and fixing issues with the code if the code is not tested properly. And any time spent on this could cost more to the organization. By implementing unit testing, we will be greatly reducing the cost incurred by an organization for a software project. This will allow the organization to spend more resources on new projects or enhancing existing projects. 

Drawbacks Of Unit Testing 

Even though unit testing is a pretty popular practice, there are a few schools of thought that find unit testing to be problematic. Here are a few such thoughts that could be considered as drawbacks of unit testing. 

  • Learning curve: It takes practice to perfect unit testing. It requires the knowledge of software design so that we can break down the code into clear and logical smaller chunks for unit testing. If not done correctly, both the unit testing and the code itself could prove difficult to maintain. 
  • Increase in the amount of code to write: Because unit testing involves writing multiple unit tests to cover all scenarios of just one code block, the amount of code we need to write increases significantly. This increases even more when the business logic we’re testing is complex. At the same time, we can’t ignore or rule out a few scenarios from testing, as this could make way for bugs. 
  • Unit testing UI could be challenging: Unit testing back-end code or business logic is pretty easy, but unit testing UI or front-end code could prove challenging as it requires us to set up browser rendering and interact with UI elements. And not all unit testing frameworks support this functionality or make it easy. 
  • Could make refactoring challenging: There might be situations when extending or refactoring existing code could become challenging. This is because unit testing kind of solidifies the structure of the code, and making any change to that code could mean rewriting the unit tests as well. This could be time consuming and could lead to burn out as well. But such cases arise when the initial design of the code was not proper. 
  • Unit testing cannot possibly catch all bugs: No matter how many unit tests we write or how many scenarios we cover, unit testing cannot cover all execution paths. Even though this thought causes a lot of debates, it could be valid in certain scenarios. 

Summary 

In this blog post, we’ve seen what unit testing is, why it is important, how to write unit tests, what to consider while writing unit tests, and some of the advantages and possible drawbacks of unit testing. All good software projects should come with unit tests so that we have the least number of bugs possible. But at the same time, unit testing is not easy. The learning curve is pretty steep, and it requires the practice of a number of other software development principles. Even though it can prove challenging, it will improve the quality of code, and give the developers the peace of mind they need. 

Not all organizations or software development teams give the importance or the time unit testing needs. But ask any team that practices unit testing correctly, and they will tell you how drastically it improves the quality of their code and gives them more time to innovate and add new features to their product instead of finding bugs and fixing them. It is recommended to spend some time on learning unit testing correctly and start implementing it in all your projects so that you can see the difference yourself. Happy coding and testing!

<<Previous: The first 4 articles in this series

  1. OOP Part 1
  2. OOP Part 2
  3. Clean, Readable Code
  4. Unit Testing Part 1

Leave a Reply

Your email address will not be published.