Doctests in Python – A Brief Overview

Python Doctests Title

Let us learn about a particular testing and debugging mechanism in Python. Doctests in Python are test cases for functions, and they can be used to verify if a function is working as intended.

What are docstrings in Python?

Before we go on to doctests, we need to learn about docstrings.

  • Docstrings are optional strings encased in triple quotes that are written as the first thing when declaring a function.
  • Docstrings are used to describe a function. We can write what a function does, how it works, the number of arguments it takes, the type of object it returns, etc.

All these things describe the function’s purpose to the programmer, and the programmer can access a function’s docstring using the __doc__ attribute.

Let us take an example of a function that prints the factorial of a number..

def factorial(num):
    """
    A function that returns the factorial of a given number.
    No. of arguments: 1, Integer
    Returns: Integer
    """
    res = 1
    for i in range(1, num+1):
        res *= i
    print(res)

As you can see, just after declaring the function, before doing anything, we write a string encased in triple quotes that describes the function.
This will make that string the documentation for that function, and accessing the attribute __doc__ will return this string. Let’s do that now.

print(factorial.__doc__)

The Output:


    A function that returns the factorial of a given number.
    No. of arguments: 1, Integer
    Returns: Integer
    

Now that we’re clear what a docstring is, we can move on to doctests.

What are doctests in Python?

As we discussed earlier, doctests in Python are test cases written inside the docstring. In our case, the factorial of 5 will be 120, so calling factorial(5) will print 120, similarly, calling factorial(0) will print 1.

These can be the test cases that we can verify for the function, and to do that, we describe them in the docstring using a syntax like this:

def factorial(num):
    """
    A function that returns the factorial of a given number.
    No. of arguments: 1, Integer
    Returns: Integer
    
    >>> factorial(5)
    120
    
    >>> factorial(0)
    1
    """
    res = 1
    for i in range(1, num+1):
        res *= i
    print(res)

If you remember the Python shell, we write all the code in the shell after the three angle brackets(>>>), and the code gets executed immediately as we press enter.

So, if we were to call factorial(5) through the Python shell, it will look exactly as we have written in the above docstring.

Specifying this in the docstring tells Python that the above lines are the expected output after running factorial(5) in the shell.

Similarly below that we have written the exact expected output for factorial(0).

Note that doctests are sensitive to white spaces and tabs, so we need to write exactly what we want as the result.

We can also specify exceptions and errors that a function may return as a result of wrong input.

Now that we have a few doctests written in our function, let us use them and check if the function works correctly.

Successful Doctests in Python

import doctest
doctest.testmod(name='factorial', verbose=True)

This is how we use doctests in Python. We import a module named doctest, and use it’s testmod function as shown.

The output will look like this:

Trying:
    factorial(5)
Expecting:
    120
ok
Trying:
    factorial(0)
Expecting:
    1
ok
1 items had no tests:
    factorial
1 items passed all tests:
   2 tests in factorial.factorial
2 tests in 2 items.
2 passed and 0 failed.
Test passed.
TestResults(failed=0, attempted=2)

As you can see, it will run every test case and check if the actual output matches the expected output. In the end, it will print the result of the testing and the programmer will be able to analyze how the function is performing.

If any of the test cases fail, it will print the exact output after the expected output and specify the number of test cases that failed at the end.

Failed Doctests in Python

Let us make doctests in Python that we know will fail:

def factorial(num):
    """
    A function that returns the factorial of a given number.
    No. of arguments: 1, Integer
    Returns: Integer
    
    >>> factorial(5)
    120
    
    >>> factorial(0)
    1
    
    >>> factorial(2)
    Two
    """
    res = 1
    for i in range(1, num+1):
        res *= i
    print(res)
    
import doctest
doctest.testmod(name='factorial', verbose=True)

In the third doctest, sending 2 will never print Two, so let us see the output:

Trying:
    factorial(5)
Expecting:
    120
ok
Trying:
    factorial(0)
Expecting:
    1
ok
Trying:
    factorial(2)
Expecting:
    Two
**********************************************************************
File "__main__", line 13, in factorial.factorial
Failed example:
    factorial(2)
Expected:
    Two
Got:
    2
1 items had no tests:
    factorial
**********************************************************************
1 items had failures:
   1 of   3 in factorial.factorial
3 tests in 2 items.
2 passed and 1 failed.
***Test Failed*** 1 failures.
TestResults(failed=1, attempted=3)

For the third test case, it failed and the module printed exactly how it failed, and at the end, we see that three test cases were attempted and one failed.

Use of Doctests in Python?

Doctests in Python are meant to be used when creating a function with an expected output in mind.

If you need a function that prints exactly something on calling with something, then you can specify it in the doctest, and at the end, the doctest module will allow you to run all the test cases at once and you will be able to see how the function performed.

The testcases mentioned should be exactly what you are expecting, if any of them fail, it indicates a bug in the function that should be rectified.

The doctests of the finished product must always be successful.

Although we cannot write every test case, it is a good idea in a big project to write the ones that are likely to fail as a result of unexpected input, like 0, 9999999, -1, or “banana”.

Conclusion

In this tutorial, we studied what doctests in Python are, how to write them, how to use them, and when to use them.

We discussed how doctests are a testing mechanism for programmers and how it makes writing test cases easy.

I hope you learned something and see you in another tutorial.