Improve recursive diff output for comparison asserts on dataclasses/attrs
See original GitHub issuepytest 6 will have a new feature (PR #6835) for displaying nicer diff output on large dataclasses. Often dataclasses have other dataclasses or other types nested inside them, and the assertion fails because one or more deeply nested fields differ. Before the diff was just on the top-level repr
of the objects, which was very hard to decipher for larger dataclasses. After the PR, the output looks like this (see https://github.com/pytest-dev/pytest/pull/6835#issuecomment-637545552):
def test_cmp():
> assert obj == obj.replace(assentior=obj.assentior.replace(culpa=7, aliquip=Aliquip.Aliquip3))
E AssertionError: assert Dignissim(assentior=Assentior(aliquip=<Aliquip.Aliquip1: 1>, culpa=6, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None), new_cupidatat=13, laoreet=1, rationibus=False) == Dignissim(assentior=Assentior(aliquip=<Aliquip.Aliquip3: 3>, culpa=7, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None), new_cupidatat=13, laoreet=1, rationibus=False)
E Matching attributes:
E ['new_cupidatat', 'laoreet', 'rationibus']
E Differing attributes:
E assentior: Assentior(aliquip=<Aliquip.Aliquip1: 1>, culpa=6, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None) != Assentior(aliquip=<Aliquip.Aliquip3: 3>, culpa=7, fugiat=Fugiat(eiusmod='aaaaaaaaaaaaaaaa', tempor='bbbbbb', incididunt='CCCCCCCCCCCCCCCCCCC', labore='dddddddddd', dolore='eeeeeeeeee', magna='fffffffffff', aliqua='gggggggggggggg', veniam=None, nostrud=None, exercitation=None, ullamco=None, laboris=None, commodo=None, consequat=None, aute=None), invenire=Invenire(irure=53, reprehenderit=153, voluptate=242, velit=100, esse=5035, cillum=53, eepcillum=422), deserunt=True, lorem=Lorem(ipsum=b'', dolor=['xx'], sit=['yy'], amet=['zz'], consectetur=b'sdfsd', adipiscing=b'adfsdf'), mollit=4294967294, laborums=[Laborum(type=23, urangulal=b'abc', ipsumal=None, occaecat=b'sdf', cupidatat=13, proident=None)], tantas='sdfsdlxcv49249sdfs90sdf==', nominati=-1, fabulas=False, tritani=None)
E
E Drill down into differing attribute assentior
E Matching attributes:
E ['fugiat',
E 'invenire',
E 'deserunt',
E 'lorem',
E 'mollit',
E 'laborums',
E 'tantas',
E 'nominati',
E 'fabulas',
E 'tritani']
E Differing attributes:
E aliquip: <Aliquip.Aliquip1: 1> != <Aliquip.Aliquip3: 3>
E
E Drill down into differing attribute aliquip
E +<Aliquip.Aliquip1: 1>
E -<Aliquip.Aliquip3: 3>
E culpa: 6 != 7
E
E Drill down into differing attribute culpa
E +6
E -7
x.py:169: AssertionError
This is a great improvement, but the drill down structure is a bit hard to see. In this case particularly, there are two differing nested attributes, aliquip
and culpa
, but the culpa
one starts just after the output of the aliquip
without any spacing, which is confusing. Also, it is not visible at which nesting level the differing attribute is.
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (6 by maintainers)
This would be an
improvement
. I noticed the CONTRIBUTING.rst document doesn’t mentionimprovement
, let me fix that 😃One idea is to use indentation for indicating the nesting level, like this:
That’s just one possible idea.