54

Is it possible to use Python's doctest concept for classes, not just functions?

If so, where shall I put the doctests - at the class' docstring, or at the constructor's docstring?

To clarify, I'm looking for something like:

class Test:
    """
    >>> a=Test(5)
    >>> a.multiply_by_2()
    10
    """
    def __init__(self, number):
        self._number=number

    def multiply_by_2(self):
        return self._number*2

Thanks in advance,

Adam

Adam Matan
  • 128,757
  • 147
  • 397
  • 562
  • 5
    As a side note, always inherit from `object` rather than nothing so that you're using *new-style classes*. – Mike Graham Apr 25 '10 at 15:16
  • 14
    Except in python 3, where there are only new-style classes, and `object` is implied with no declared parents. – Daenyth Oct 14 '10 at 18:36

4 Answers4

78

Instead of instantiating the object in every method, you can use the extraglobs argument:

class Test:
    def multiply_by_2(self):
        """
        >>> t.multiply_by_2()
        10
        """
        return self._number*2

if __name__ == '__main__':
    import doctest
    doctest.testmod(extraglobs={'t': Test()})
Asclepius
  • 57,944
  • 17
  • 167
  • 143
Ari
  • 2,311
  • 1
  • 17
  • 17
30

You're missing the code to actually run the doctests at the bottom of the file:

class Test:
    <snip>

if __name__ == "__main__":
    import doctest
    doctest.testmod()

As for where to put the tests:

  • If it's testing the class as a whole, I'd put them in the class' docstring.
  • If it's testing the constructor, I'd put them in the constructor's docstring.
  • If it's testing a method (as it seems to be in this case), I'd actually put it them in that method's docstring.
Aaron Maenpaa
  • 119,832
  • 11
  • 95
  • 108
  • 1
    +1 Thanks! However, documenting a method seems a bit tedious, because it requires me to initialize an object for each doctest. – Adam Matan Apr 25 '10 at 12:58
  • 4
    @Adam: if you need more sophisticated testing, use the unittest module. doctest is primarily intended for testing the documentations, not testing the code. – Lie Ryan Apr 25 '10 at 13:59
7

The doctest module looks for any docstrings in a file and executes any embedded code in it, so yes it is possible to use doctest for classes.

As for whether it is better to put the doctests in the class's docstring or the constructor, I think that depends on what exactly you are documenting.

If the docstring gives a general overview of the class and how to use it then I think it is better to put it in the class.

If the docstring is specifically about how to create instances of the class then it should go in the __init__ method.

Remember the intent of doctests is primarily to have self-validating example code in documentation, so IMHO the documentation aspect should take priority over the testing aspect.

Edit:

In your example above there is no code to execute the doctest - running python test.py -v will execute the main python code which just defines the class.

You need to add this to the end of the file:

if __name__ == "__main__":
    import doctest
    doctest.testmod()

Alternatively If you are using Python 2.6 or later run it with:

python -m doctest -v test.py
Dave Kirby
  • 25,806
  • 5
  • 67
  • 84
  • Thanks, but why isn't my example working? I've tried `python test.py -v`, and got nothing – Adam Matan Apr 25 '10 at 12:38
  • 1
    @Adam If what you posted is the full `test.py` file, you didn't do `if __name__ == "__main__": import doctest; doctest.testmod()`; if you did use that, no output means no errors. :) – badp Apr 25 '10 at 12:46
0

I think the doctest module documentation does not explain how to handle this, and it should do better at explaining what to do.

A pattern that I figured out for testing class methods that don't need instance data, but might need access to class data, is to pass in the Class object instead of an instance.

class Test:
    """
    >>> Test.multiply_by_3(Test,2)
    6
    """
    def __init__(self, number):
        self._number=number

    _THREE = 3
    def multiply_by_3(self, x):
        return x*self._THREE

if __name__ == "__main__":
    import doctest
    doctest.testmod()
Jim DeLaHunt
  • 10,960
  • 3
  • 45
  • 74