One of the questions I often ask during interviews after candidates write code, is to come up with test cases for unit testing. This question helps me to tell experienced and good developers apart from non-experienced ones. Non-experienced developers would have test cases based only on code coverage. It isn’t wrong, just insufficient, because if there is a condition that the code is not already checking, the tests will not surface the defect.

Let me explain with a simple example. Let’s say here’s the Java code for binary search.

public void Node {
    public int value;
    public Node left;
    public Node right;
}

public Node binarySearch(Node root, int value) {
    if (root.value == value) {
        return root;
    } else if (root.value < value) {
        return binarySearch(root.left, value);
    } else {
        return binarySearch(root.right, value);
    }
}

If I only have test cases for the above code based on code coverage, I will have the following test cases:

  • value is at the root
  • value is on the left subtree
  • value is on the right subtree
  • tree is one-sided (only have left children or right children)
  • tree is zig-zaged

Everything looks good right? What’s missing here? What about if the tree is null or empty, or that the value does not exist in the tree? Ops. It’s not in the code, so it’s not covered in the test cases.

Test cases need to be designed independently of the code in front of them. Yes, it is important to cover all paths of execution, but it’s equally important to test the correctness of the code based on requirement, including all boundary and error cases.

So next time, think outside the code.