Using Python docstring and doctest for simple Documentation and Testing

June 30, 2020

Introduction: Python doctest module example.


Python doctest module is a way to test simple Python functions via the interactive shell output. It simply searches through a set of text, such as docstring, and looks for what appears to be interactive Python sessions, and executes those sessions to compare the output. It is a pretty low overhead way to provide testing without writing separate tests using unittest or the pytest framework.

Let's see an example. We wrote the following function in doctest_example.py to test if the number passed in is divisible by 5 (for more information on the modulus operator) :

def divisible_by_5(number):
....if number % 5 == 0:
........print(f'{number} is divisible by 5')
....else:
........print(f'{number} is not divisible by 5')

if __name__ == "__main__":
....divisible_by_5(10)

....divisible_by_5(12)

We can execute this script as a standalone script:

$ python doctest_example.py

10 is divisible by 5

12 is not divisible by 5

Or we can import this file as a module:

$ python doctest_example.py

10 is divisible by 5

12 is not divisible by 5

Sometimes this is good enough. But how about let's add some docstring to help so we can know what this script does, wich some interactive shell prompt for illustration. We do this by adding the multi-line comments using triple ' symbol pairs:

def divisible_by_5(number):
....'''
....This fucntion test if the number passed in is divisible by 5.
....>>> divisible_by_5(20)
....20 is divisible by 5
....>>> divisible_by_5(21)
....21 is not divisible by 5
....'''
....if number % 5 == 0:
........print(f'{number} is divisible by 5')
....else:

........print(f'{number} is not divisible by 5')

Besides better readability, we can now use the 'help' function to understand our function:

>>> from doctest_example import divisible_by_5

>>> help(divisible_by_5)

Help on function divisible_by_5 in module doctest_example:

divisible_by_5(number)

This fucntion test if the number passed in is divisible by 5.

>>> divisible_by_5(20)

20 is divisible by 5

>>> divisible_by_5(21)

21 is not divisible by 5

Now we can use the doctest module to perform simple test:

$ python -m doctest doctest_example.py

If the output matches, there is no output. But let's say we change the docstring output to the following:

def divisible_by_5(number):
....'''
....This fucntion test if the number passed in is divisible by 5.
....>>> divisible_by_5(20)
....20 is divisible by 5, I love it!

....>>> divisible_by_5(21)
....21 is not divisible by 5
....'''

<skip>

Upon running doctest, we will receive the following error:

$ python -m doctest doctest_example.py

**********************************************************************

File "/Users/echou/Desktop/NetworkAutomation_Ninja/Blog_Related/doctest/doctest_example.py", line 6, in doctest_example.divisible_by_5

Failed example:

divisible_by_5(20)

Expected:

20 is divisible by 5, I love it!

Got:

20 is divisible by 5

**********************************************************************

1 items had failures:

1 of 2 in doctest_example.divisible_by_5

***Test Failed*** 1 failures.

Pretty neat, right? Documentation and testing in one shot, that is what I call efficiency. Of course, this works well with small functions that can fit everything in a few lines; more complex programs and functions require separate documentation and more in-depth testing.

I hope this was helpful to you. Leave me a note to let me know what you think!

Happy Coding,

Eric

Return to blog