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__])

results matching ""

    No results matching ""