Testing Got Easy with Python Doctest

Hello Everyone! I hope all are doing great. So, today in this article, we will discuss the python doctest module. We will see what docstrings are and how they help us write clear, understandable, and testable codes. It provides us a way to use the interactive mode of python in script mode with the help of docstrings. Let’s understand them one by one.

Python Docstrings

So, python docstrings are the kind of strings declared using triple single quotes or triple-double quotes. The primary function of docstrings is to associate documentation with python modules, python functions, or python classes. Unlike comments, docstrings are used to describe the use of the function rather than explaining how it works.

It is advised to use docstrings for each function to explain its use. And we can access them by using either of the ways.

  • __doc__ method of the object :- Ex: <function_name>.__doc__
  • help() :- help(<function_name>)

However, it is compulsory to use docstrings while testing the file using the python doctest module. We won’t go into detail about it as this is enough to understand the doctest module. So let’s move on to that.

Doctest Module

Doctest Module gives us the option to test our file by using docstrings. It looks for the code section in docstrings, which appears like an interactive python shell. Then it runs the given samples on those sections of code and returns whether it passed the test or failed it. There are several ways to use doctest module. We will see each of them and better understand each part.

Example 1:- Defining Docstrings within same file

In this example, we will see how we can use docstrings to test the file while executing them. To can do that by following the given steps.

  • Step 1: Create docstrings by using triple single quotes or triple double quotes with the function we want to test.
  • Step 2: Call the function as python interactive shell mode starting with “>>>”.
  • Step3: Define the output in next line.
  • Step4: Run the file by using command,
python -m doctest -v filename.py

Let’s take an example.

"""
Examples on Doctest
"""

def addition_function(obj1,obj2):
    """
    >>> addition_function(5,2)
    7
    >>> addition_function('Python','Pool')
    'PythonPool'
    """    
    return obj1+obj2

Output:

Trying:
    addition_function(5,2)
Expecting:
    7
ok
Trying:
    addition_function('Python','Pool')
Expecting:
    'PythonPool'
ok
1 items had no tests:
    2
1 items passed all tests:
   2 tests in 2.addition_function
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

Now, think, what if one of the test cases failed. Let’s check that now.

"""
Examples on Doctest
"""

def addition_function(obj1,obj2):
    """
    >>> addition_function(5,2)
    8   
    >>> addition_function('Python','Pool')
    'PythonPool'
    """    
    return obj1+obj2

Output:

Trying:
    addition_function(5,2)
Expecting:
    8
**********************************************************************
File "C:\Users\Rishav Raj\Documents\filename.py", line 7, in 2.addition_function
Failed example:
    addition_function(5,2)
Expected:
    8
Got:
    7
Trying:
    addition_function('Python','Pool')
Expecting:
    'PythonPool'
ok
1 items had no tests:
    2
**********************************************************************
1 items had failures:
   1 of   2 in 2.addition_function
2 tests in 2 items.
1 passed and 1 failed.
***Test Failed*** 1 failures.

So, if a test case fails, it shows which test case is failed and the whole description about that.

Example 2:- Defining Docstrings within same Modules

In the above example, we have seen how we can define a docstring within the same file and then test it. Now, we will see how we can maintain a separate file for testing purposes. The procedure for defining the docstring is as above. We need to import functions from other classes and define docstrings there. Let’s see an example of that.

First, we will create a sample.py file that contains the function we want to test, and then we create a test.py file for the testing. We will use the following command to run the testing file.

python -m doctest -v test.py
# sample.py file
def multiplication_function(a,b):
    return a*b
# Test.py file

#Using Python interactive shell mode
"""
>>> from sample import *
>>> multiplication_function(8,2)
16
>>> multiplication_function('PythonPool',2)
'PythonPoolPythonPool'
"""

Output:

Trying:
    from sample import *
Expecting nothing
ok
Trying:
    multiplication_function(8,2)
Expecting:
    16
ok
Trying:
    multiplication_function('PythonPool',2)
Expecting:
    'PythonPoolPythonPool'
ok
1 items passed all tests:
   3 tests in test
3 tests in 1 items.
3 passed and 0 failed.
Test passed.

Example 3:- Using testmode() function

We can also check the test cases using the testmode method. We have to pass the function (we want to test) as an argument of the testmode function. Let’s take an example to understand it clearly.

# import testmod
from doctest import testmod

def factorial_function(num):
    """
    >>> factorial_function(5)
    120
    """   
    factorial = 1
    if num < 0:
        print("Number should be greater than 0")
    elif num == 0:
        print("The factorial of 0 is 1")
    else:
        for i in range(1,num + 1):
            factorial = factorial*i
        print(factorial)

Output:

Trying:
    factorial_function(5)
Expecting:
    120
ok
1 items had no tests:
    factorial_function
1 items passed all tests:
   1 tests in factorial_function.factorial_function
1 tests in 2 items.
1 passed and 0 failed.
Test passed.

In the above example, we have the second argument named verbose, which should be “true” to get the test results on our output screen.

DocTest Vs UnitTest

Comparing both is a kind of injustice to them as both serve different purposes. Doctest is used when we need to give examples of our code for informative documentation, while the goal of the unitest is to thoroughly test every case rather than illustrate what it does by the example. Moreover, we can also say that by using doctest, we can also check the proper documentation only based on the code, which is not possible in the unittest framework.

How to Skip Entire Block in Python Doctest

You can also skip the entire block within the docstring while doctesting. To do that, You have to change “>>>” to “>>” and “…” to “..”. So,

"""
>>> multiplication_function(8,2)
16
>>> multiplication_function('PythonPool',2)
'PythonPoolPythonPool'
"""

becomes:

"""
>> multiplication_function(8,2)
16
>> multiplication_function('PythonPool',2)
'PythonPoolPythonPool'
"""

FAQs

Q1) How to make reusable objects in python doctest?

You can use testmod(extraglobs={:}) it to define a reusable object globally.
testmod(extraglobs={'f': initFileGenerator('')})

Q2) Is it possible to run python doctest on a jupyter cell function?

Yes, You can use them in the jupyter cell function by using testmode method as discussed above.

Q3) How to write a python doctest that is os independent regarding path separator?

You can use the “os.path.join()” method to do that.
e.g.
>>> relative_path == os.path.join('bar', 'foobar')
True

Q4)Can you check that an exception is thrown with doctest in python?

Yes, we can do that. Let’s see the following example to understand it.

>>> x
Traceback (most recent call last):
...
NameError: name 'x' is not defined

By defining the exception after the shell mode, you can check the error in doctest mode.

Conclusion

So, today in this article, we have seen how we can use the testing functionality provided by python doctest to test our file. We have witnessed docstrings and how we can use those for our testing purpose. At the end of the article, we have seen the different ways of testing our file.

I hope this article has helped you. Thank You.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments