[Python-ideas] Fwd: doctest
Steven D'Aprano
steve at pearwood.info
Tue Feb 28 08:59:56 CET 2012
On Mon, Feb 27, 2012 at 12:28:17PM -0700, Mark Janssen wrote:
> On Mon, Feb 20, 2012 at 11:28 AM, Barry Warsaw <barry at python.org> wrote:
> > On Feb 17, 2012, at 02:57 PM, Mark Janssen wrote:
> > FWIW, I think doctests are fantastic and I use them all the time. There are
> > IMO a couple of things to keep in mind:
> >
> > - doctests are documentation first. Specifically, they are testable
> > documentation. What better way to ensure that your documentation is
> > accurate and up-to-date? (And no, I do not generally find skew between the
> > code and the separate-file documentation.)
I second what Barry says here. Doctests are for documentation, or at
least, doctests in docstrings are for documentation, which means they
should be simple and minimal, and only cover the most important parts of
your function. Certainly they should only cover the interface, and never
the implementation, so not used for regression testing or coverage of
odd corner cases.
I have no problem with extensive doctests if they are put in an external
document. I do this myself. But when I call help(func), I want to learn
how to use func, not see seven pages of tests that don't help me
understand how to use the function.
> > My doctests usually describe mostly the good path through the API.
> > Occasionally I'll describe error modes if I think those are important for
> > understanding how to use the code. However, for all those fuzzy corner cases,
> > weird behaviors, bug fixes, etc., unittests are much better suited because
> > ensuring you've fixed these problems and don't regress in the future doesn't
> > help the narrative very much.
And again, +1 with what Barry says here, which means I disagree with
your response:
> I think is an example of (mal)adapting to an incomplete module, rather
> than fixing it. I think doctest can handle all the points you're
> making. See clarification pointers below...
I don't accept this argument. Doctest is designed for including example
code in documentation, and ensuring that the examples are correct. For
that, it does a very good job. It makes a great hammer. Don't use it
when you need a spanner.
It's not that doctest can't handle regression tests, but that regression
tests shouldn't be put inside the the function docstring. Why should
people see a test case for some bug that occurred three versions back
in the documentation? Put it in a separate test suite, either unit
tests, or a literate programming doc using doctest. Don't pollute the
docstring with tests that aren't useful documentation.
When people read your docstring, you have to expect that they are
reading it in isolation. They want to know "How do I use function
spam?", and any example code should show them how to use function spam:
>>> data = (23, 42, 'foo', 9)
>>> collector = [5]
>>> spam(data, collector)
>>> collector
[5, 28, 70, None, 79]
That works as documentation first, and as a test second. This does not:
>>> spam(data, collector) # data and collector defined elsewhere
>>> collector
[5, 28, 70, None, 79]
The fact that each docstring sees a fresh execution context is a
good thing, not a bug.
> >>1. Execution context determined by outer-scope doctest defintions.
> >
> > Can you explain this one?
>
> I gave an example in a prior message on this thread, dated Feb 17. I
> think it's clear there but let me know.
>
> Basically, the idea is that since the class def can also have a
> docstring, where better would setup and teardown code go to provide
> the execution context of the inner method docstrings?
Is that a trick question? I don't want docstrings to have automatic
setup and teardown code at all, and if they did, I certainly don't want
them to be in some other object's docstring (related or not).
> Now the question: is it useful or appropriate to put setup and
> teardown code in a classdef docstring?
In my opinion, no, neither useful nor appropriate. It would be
counter-productive, by reducing the value of documentation as
documentation, while still being insufficiently powerful to replace unit
tests.
A big -1 on this.
[...]
> Well, hopefully, I've convinced you a little that the limitations in
> doctests over unittests are almost, if not entirely due, to the
> incompleteness of the module. If the two items I mentioned were
> implemented I think it would be far superior to unittest.
I already think that doctest is far superior to unittest, for testing
executable examples in documentation. I don't think it is superior to
unittest for unit testing, or regression testing. Nor is it inferior --
its just different.
> (Corner
> cases, etc can all find a place, because every corner case should be
> documented somewhere anyway!!)
I think you have a different idea of "corner case" than I do.
Corner cases, in my experience, refer to the implementation: does the
function work correctly when the input is in the corner? Since this is
testing the implementation, it shouldn't be in the documentation.
The classic example is, does this list-function work when the list is
empty? So I would expect that the unit tests for, say, the sorted()
built-in will include a test case for sorted([]). (This applies
regardless of whether you use unittest, doctest, nose, or some other
testing framework.)
But the documentation for sorted() don't need to explicitly state that
sorting an empty list returns an empty list. That's a given from the
accepted meaning of sorting -- if there's nothing to sort, you get
nothing. Nor does it need to explicitly state that sorting a list with
one item returns a list with one item. A single example of sorting a
list of (say) four items is sufficient to document the purpose of
sorted(), but it would be completely insufficient for unit testing
purposes.
--
Steven
More information about the Python-ideas
mailing list