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
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.
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
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
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”.
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.