6

I have a Python3 function that returns a multi-line string. I want to test it with doctest, but can't get it to work. I've tried using the +NORMALIZE_WHITESPACE directive without success.

def dummy():
    """Dummy test function

    >>> dummy()  # doctest: +NORMALIZE_WHITESPACE
    '''Hello Mum
    Hello Dad'''
    """
    return """Hello Mum
    Hello Dad"""

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

The above code fails with this error:

File "c:/foo.py", line 5, in __main__.dummy
Failed example:
    dummy()  # doctest: +NORMALIZE_WHITESPACE
Expected:
    '''Hello Mum
    Hello Dad'''
Got:
    'Hello Mum\n    Hello Dad'

I have tried changing the doctest line to include a literal \n as follows:

>>> dummy()  # doctest: +NORMALIZE_WHITESPACE
'Hello Mum\n    Hello Dad'

But this fails with an almost identical error:

Failed example:
    dummy()  # doctest: +NORMALIZE_WHITESPACE
Expected:
    'Hello Mum
    Hello Dad'
Got:
    'Hello Mum\n    Hello Dad'

How can I test a multi-line string output with doctest?

Justin
  • 1,980
  • 1
  • 16
  • 25
  • Have you tried [this](https://stackoverflow.com/a/8594009/12096138) ? – ImranD Oct 11 '19 at 10:06
  • Thanks @ImranD. It looks like the ellipsis in that example is for multi-line input (not output), but I have tried various permutations including without quotes (as is shown in your suggestion) but always get the literal `\n` in the the "Got:" rather than an actual line break... – Justin Oct 11 '19 at 10:13

2 Answers2

4

As mentioned in my comment, a work around so you don't need \n literals in the doctest string is to print the output and then match on that, e.g.

def dummy():
    """Dummy test function

    >>> a = dummy()
    >>> print(a)
    Hello Mum
    Hello Dad

    """
    return """Hello Mum
Hello Dad"""

if __name__ == "__main__":
    import doctest
    doctest.testmod()
Justin
  • 1,980
  • 1
  • 16
  • 25
3

The problem is that the \n is being interpreted as a newline in the docstring, but doctest needs a literal backslash in there.

The doctest help says (emphasis mine):

If you continue a line via backslashing in an interactive session, or for any other reason use a backslash, you should use a raw docstring, which will preserve your backslashes exactly as you type them

So what they suggest is the following (note the r before the """):

def dummy():
    r"""Dummy test function

    >>> dummy()
    'Hello Mum\n    Hello Dad'
    """
    return """Hello Mum
    Hello Dad"""

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

It would also work without the leading r if you were to use a \\n instead of \n, but it's probably better to stick with the r version that's copy-pasteable from a python REPL.

Matthew Strawbridge
  • 19,940
  • 10
  • 72
  • 93
  • 1
    Thanks Matthew! I had hoped there's a way for the doctest expected-output to "look like" the actual output, since in my real-life example I have 6 or 7 lines, which is very hard to read in the doctest on a single line. Looking at Imran's [example](https://stackoverflow.com/a/8594009/12096138) shows you can achieve this if you `print` the output, so perhaps that's the only work around? – Justin Oct 13 '19 at 11:18
  • Are you missing an empty line before the second triple double quote-marks `"""` (above `return`)? – PatrickT May 13 '20 at 08:57