doctest module
Date: January 13th 2016
Last updated: January 13th 2016
Testing plays an important role in developing a program. Until now, I have underestimated how important it is. I recommend this 30 minute screencast and write-up on testing by Atlassian.
My strategy to-date has been to write a function, run the function in an interactive session and hunt for exceptions. If the code did what I wanted then I would stop testing. This style is testing functional correctness (e.g. automation) of a single isolated function, which falls under the umbrella of unit testing. I should point out that this doesn't test important features like security and usability. I will return to these points when I look at systems testing.
The doctest module is one tool that can help unit testing. The doctest module searches doc strings for an interactive session prompt ( >>> ). When it finds one, python will execute the code and determine if the following new line contains the expected output. If the function passes the test you get nothing. If it fails, the output will show what was expected and what it got from processing the doc string.
When a function is working, import it into an interactive session and trial some examples. See the notes in the code below for implementation of doctest. Then, update the doc string by copy and pasting the input/output. The doctest should still return nothing after doing this.
Now when changes happen to the function these doctests can verify that it's still working as expected. In the example below the code counts the number of vowels in a string. If for example, the vowel list is updated to include 'y', the doctest will raise an error(e.g. y is the only english letter used as a vowel and a consonant).
# vowels.py
def count_vowels(x):
"""
Returns the number of vowels in a string.
>>> count_vowels('testing this module with your text')
10 # if y is excluded
11 # if y is included
"""
count = 0 # Set counter to zero
vowels = ['a', 'e', 'i', 'o', 'u'] # list of vowels
# loop through each letter of the string
for i in x:
# match each letter to list of vowels
if i in vowels:
count += 1 # increase counter by 1
return count
# Notes on the if statement below:
#
# This is NOT executed if 'vowels' is imported to python
# E.g. >>> python; import vowels
# (assuming python started in the same folder as vowels.py)
#
# This IS executed if run as a main program
# E.g. >>> python vowels.py
if __name__ == "__main__":
import doctest
doctest.testmod()
To run doctest for a class...
The first docstring prompt instantiates the class CountVowels. The following docstring prompt calls the count_vowels method. More on classes, self and object oriented programming later.
# vowels.py
class CountVowels:
def __init__(self, sentence):
self.sentence = sentence
def count_vowels(self):
"""
Returns the number of vowels in a string.
>>> cv = CountVowels('testing this module')
>>> cv.count_vowels()
6
"""
self.count = 0 # Set counter to zero
vowels = ['a', 'e', 'i', 'o', 'u'] # list of vowels
# loop through each letter of the string
for i in self.sentence:
# match each letter to list of vowels
if i in vowels:
self.count += 1 # increase counter by 1
return self.count
if __name__ == "__main__":
import doctest, sys
# Notes:
# every class gets a module attribute
# as new modules are imported they get added to sys.modules
# __name__ refers to the scripts runtime name
# sys.modules is looking for a dictionary key
# __name__ == "vowels" as this script is called vowels.py
doctest.testmod(sys.modules[__name__])