47

I'm writing a doctest for a function that outputs a dictionary. The doctest looks like

>>> my_function()
{'this': 'is', 'a': 'dictionary'}

When I run it, it fails with

Expected:
    {'this': 'is', 'a': 'dictionary'}
Got:
    {'a': 'dictionary', 'this': 'is'}

My best guess as to the cause of this failure is that doctest isn't checking dictionary equality, but __repr__ equality. This post indicates that there's some way to trick doctest into checking dictionary equality. How can I do this?

funky-future
  • 3,716
  • 1
  • 30
  • 43
Emmett Butler
  • 5,969
  • 2
  • 29
  • 47
  • since dict is unordered, you can't use the dict as it is. you must transform it into an ordered object – ornoone Mar 21 '13 at 14:00
  • 2
    The answers listed below are all in the doctest documentation: http://docs.python.org/2/library/doctest.html#warnings – Robᵩ Mar 21 '13 at 14:10
  • @ornoone But why? They are equal objects, that's what doctest should be checking. – endolith Aug 27 '14 at 23:27
  • as said in accepted answer, it is the __repr__ for your both object that is checked, not their content. since `repr(a) != repr(b)` doctest think your objects is differents. event if `a == b` is ok. i think that is so because doctest is in __doc__, and should be easyly readable and with the repr check, it is readable. – ornoone Nov 05 '14 at 14:50

7 Answers7

41

Another good way is to use pprint (in the standard library).

>>> import pprint
>>> pprint.pprint({"second": 1, "first": 0})
{'first': 0, 'second': 1}

According to its source code, it's sorting dicts for you:

http://hg.python.org/cpython/file/2.7/Lib/pprint.py#l158

items = _sorted(object.items())
charlax
  • 25,125
  • 19
  • 60
  • 71
  • 11
    it would be nice, but python devs [don't recommend this](https://bugs.python.org/issue20310) because they don't guarantee pprint stability across versions. – max Feb 12 '16 at 08:08
  • also, this solution necessarily work for other datatypes (such as set). – hardmooth Aug 21 '17 at 04:31
  • 1
    Advantage here is once the assertion fails, `pprint` will show a helful diff. If `pprint` implementation changes between Python version, well, we need to adjust our tests or just duplicate the implementation in our code to keep it stable. – geekQ Oct 06 '17 at 07:48
31

Doctest doesn't check __repr__ equality, per se, it just checks that the output is exactly the same. You have to ensure that whatever is printed will be the same for the same dictionary. You can do that with this one-liner:

>>> sorted(my_function().items())
[('a', 'dictionary'), ('this', 'is')]

Although this variation on your solution might be cleaner:

>>> my_function() == {'this': 'is', 'a': 'dictionary'}
True
Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 4
    Your solution is cleaner but it will fail to tell you what my_function actually evaluated to. – Jono Jan 06 '14 at 06:44
  • 1
    the pprint solution looks much cleaner, see charlax's answer – Alojz Janez Apr 24 '14 at 01:16
  • 1
    but what if it's a documentation example and I want to show a realistic input and realistic output? Is there a way to get it to check equality instead of exact strings? – endolith Aug 27 '14 at 23:34
  • The second variation you gave worked perfectly for my simple doctest. Thanks! – allardbrain Jul 17 '17 at 03:22
  • 1
    the `pprint`-solution doesn't always work for `set` doctests - no guaranteed order. If you want to know afterwards what It was evaluated to, you can do `my_function() == {'this': 'is', 'a': 'dictionary'} or my_function()` – hardmooth Aug 18 '17 at 07:32
16

I ended up using this. Hacky, but it works.

>>> p = my_function()
>>> {'this': 'is', 'a': 'dictionary'} == p
True
Emmett Butler
  • 5,969
  • 2
  • 29
  • 47
  • 3
    I don't think that's hacky (although I'd write `p == {etc}`) -- that's the first recommended technique in the relevant section of [the docs](http://docs.python.org/3/library/doctest.html#warnings). – DSM Mar 21 '13 at 14:02
  • 6
    why not `my_function() == {'this': 'is', 'a': 'dictionary'}`? – endolith Aug 27 '14 at 23:34
  • 2
    The disadvantage here is once the assertion fails, you do not know which keys, values exactly are wrong. Solution with `pprint` would show a helpful diff. – geekQ Oct 06 '17 at 07:38
3

turn it into a list via dict.items() and then sort it ...

>>> l = my_function().items()
>>> l.sort()
>>> l
[('a', 'dictionary'), ('this', 'is')]
ornoone
  • 651
  • 5
  • 11
3

You can create an instance of unittest.TestCase class inside your doctests, and use it to compare dictionaries:

def my_function(x):
    """
    >>> from unittest import TestCase
    >>> t = TestCase()

    >>> t.assertDictEqual(
    ...     my_function('a'),
    ...     {'this': 'is', 'a': 'dictionary'}
    ... )

    >>> t.assertDictEqual(
    ...     my_function('b'),
    ...     {'this': 'is', 'b': 'dictionary'}
    ... )

    """
    return {'this': 'is', x: 'dictionary'}

Note: this approach is better than simply checking if dictionaries are equal, because it will show diff between the two dictionaries.

David Avsajanishvili
  • 7,678
  • 2
  • 22
  • 24
2

Most of it has been already said here.. anyway JSYK: there is a dedicated section in doctest documentation:

https://docs.python.org/3.5/library/doctest.html#warnings

iago-lito
  • 3,098
  • 3
  • 29
  • 54
1

I found it useful to use the deepdiff package in my doctests when testing arbitrarily nested data. For example:

def something_complicated():
    """
    >>> from deepdiff import DeepDiff
    >>> DeepDiff(something_complicated(),
    ...          {'expected': {'output': ['a', 'b', 'c']}},
    ...          ignore_order=True)
    {}
    """
    items = ['a', 'b', 'c']
    random.shuffle(items)
    return {'expected': {'output': items}}
brianmaissy
  • 394
  • 5
  • 9