Skip to content

Instantly share code, notes, and snippets.

@djay
Last active April 11, 2022 09:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djay/c047a90622b3dae86be5d819e8dfda12 to your computer and use it in GitHub Desktop.
Save djay/c047a90622b3dae86be5d819e8dfda12 to your computer and use it in GitHub Desktop.
How to keep your doctests and isolate them too

How to keep your doctests and isolate them too

Let's say you have a project with a long doctest .txt file thats grown and grown. I like doctests for their readability however they tend to encourge tacking on semi related tests to a big txt file. Debugging now takes longer and one failing test results in masking other errors. This is why smaller isolated tests are good.

However you can quickly improve things.

mytest.txt (original)

Let's say your original doctest was something like

>>> plus = lambda a,b: a*b
>>> plus(1, 1)
2
>>> plus(2, 2)
4

test_plus.py (step1)

First rename it to .py, wrap your docttest in a function docstring using r''' ''' like below. Using r''' is the least likely to interfer with any quotes or newlines in the tests.

Copy the DocFileSuite (possibly in a file called test_docttests.py to the bottom of the file and change it to DocTestSuite and remove the .txt file references. A DocTestSuite with no module reference scans itself.

def test_plus():
  r'''
>>> plus = lambda a,b: a*b
>>> plus(1, 1)
2
>>> plus(2, 2)
4
'''

def test_suite():
    return doctest.DocTestSuite(
        globs={},
    )

This test will now work exactly the same as the old one.

test_plus.py (isolated)

Now you can split the tests up and move the setup into a seperate function. This is harder work as your tests might not be that independent with side affects from earlier tests being required for later tests to pass.

You can have a seperate setup and teardown functions.

def setup(suite):
  plus = lambda a,b: a*b
  suite.globs.update(locals())

def test_plus_one():
  r'''
>>> plus(1, 1)
2
'''

def test_plus_two():
  r'''
>>> plus(2, 2)
4
'''

def test_suite():
    return doctest.DocTestSuite(
        globs={},
        setUp=setup,
    )

There might be a nicer way to do this that would allow you to mix and match between unittest type tests and doctests but at this point they are functionationally the same and IMO you don't get much out of rewriting these as unittests other than having to write a lot of self.assertRegex everywhere. The goal was to have the test more maintainable and make it easier to work on specific bugs and this is achieved with minimal effort

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment