Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any ideas how to improve readability of test output on failed matches? #2

Open
bluenote10 opened this issue Aug 15, 2023 · 0 comments

Comments

@bluenote10
Copy link

First of all thanks for this nice projects, look very useful and well written!

I'm wondering if you are aware of any tricks to make the output of a failed match within pytest easier to read. Here is a contrived example, simulating a large-ish payload that gets matched against some matcher.

from anys import AnyMatch

LONG_TEXT_EXAMPLE = """
Example of a long multi-line text.
Example of a long multi-line text.
Example of a long multi-line text.
"""

def test_example():
    payload = {
        "foo": LONG_TEXT_EXAMPLE,
        "bar": LONG_TEXT_EXAMPLE,
        "baz": LONG_TEXT_EXAMPLE,
    }
    assert payload == {
        "foo": LONG_TEXT_EXAMPLE,
        "bar": AnyMatch("(.*)looong(.*)"), # doesn't match; mistake on purpose
        "baz": LONG_TEXT_EXAMPLE,
    }

Running a plain pytest shows that the matching has failed, but the output doesn't make it obvious that it was this bar matcher:

>       assert payload == {"foo": "asjdfhlaksfdh", "bar": AnyMatch("(.*)looong(.*)")}
E       AssertionError: assert {'bar': '\nEx...sjdfhlaksfdh'} == {'bar': AnyMa...sjdfhlaksfdh'}
E         Omitting 1 identical items, use -vv to show
E         Differing items:
E         {'bar': '\nExample of a long multi-line text.\nExample of a long multi-line text.\nExample of a long multi-line text.\n'} != {'bar': AnyMatch('(.*)looong(.*)')}
E         Left contains 1 more item:
E         {'baz': '\n'
E                 'Example of a long multi-line text.\n'
E                 'Example of a long multi-line text.\n'...
E         
E         ...Full output truncated (3 lines hidden), use '-vv' to show

test_test.py:17: AssertionError
=============================================================================================================================================== short test summary info ===============================================================================================================================================
FAILED test_test.py::test_example - AssertionError: assert {'bar': '\nEx...sjdfhlaksfdh'} == {'bar': AnyMa...sjdfhlaksfdh'}
================================================================================================================================================== 1 failed in 0.06s ==================================================================================================================================================

Using pytest -vv contains the information in a sense, but the usage of the + and - in the diff still confuse me a little bit.

>       assert payload == {"foo": "asjdfhlaksfdh", "bar": AnyMatch("(.*)looong(.*)")}
E       AssertionError: assert {'foo': 'asjdfhlaksfdh', 'bar': '\nExample of a long multi-line text.\nExample of a long multi-line text.\nExample of a long multi-line text.\n', 'baz': '\nExample of a long multi-line text.\nExample of a long multi-line text.\nExample of a long multi-line text.\n'} == {'foo': 'asjdfhlaksfdh', 'bar': AnyMatch('(.*)looong(.*)')}
E         Common items:
E         {'foo': 'asjdfhlaksfdh'}
E         Differing items:
E         {'bar': '\nExample of a long multi-line text.\nExample of a long multi-line text.\nExample of a long multi-line text.\n'} != {'bar': AnyMatch('(.*)looong(.*)')}
E         Left contains 1 more item:
E         {'baz': '\n'
E                 'Example of a long multi-line text.\n'
E                 'Example of a long multi-line text.\n'
E                 'Example of a long multi-line text.\n'}
E         Full diff:
E           {
E         -  'bar': AnyMatch('(.*)looong(.*)'),
E         +  'bar': '\n'
E         +         'Example of a long multi-line text.\n'
E         +         'Example of a long multi-line text.\n'
E         +         'Example of a long multi-line text.\n',
E         +  'baz': '\n'
E         +         'Example of a long multi-line text.\n'
E         +         'Example of a long multi-line text.\n'
E         +         'Example of a long multi-line text.\n',
E            'foo': 'asjdfhlaksfdh',
E           }

test_test.py:17: AssertionError
=============================================================================================================================================== short test summary info ===============================================================================================================================================
FAILED test_test.py::test_example - AssertionError: assert {'foo': 'asjdfhlaksfdh', 'bar': '\nExample of a long multi-line text.\nExample of a long multi-line text.\nExample of a long multi-line text.\n', 'baz': '\nExample of a long multi-line text.\nExample of a long multi-line text.\nExample of a long multi-line text.\n'} == {'foo': 'asjd...
================================================================================================================================================== 1 failed in 0.06s ==================================================================================================================================================

In the end this is probably just how pytest works after an == comparison has failed, but maybe you have some other ideas how to improve the readability.

What if the library would offer its own assert_eq(a, b) helper (drawing some inspiration here from the great rust-pretty-assertions in Rust)? Then it could either:

  • format the diff itself, with an understanding of the matchers, but that could get tough with all the pretty printing.
  • the __eq__ implementation of the matchers could -- as a side effect (dirty...) -- collect all failed matchers in a local context. Then the assert_eq function could simply run a == b and after the comparison is done, it could look into that "local matcher errors collection" if it contains errors and display a more specific explanation why certain matchers have failed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant