Skip to main content

Test equality of unordered collections in pytest

Project description

pytest-unordered: Test collection content, ignoring order

Build Status Coverage Status Language Python Compatibility PyPI

pytest_unordered allows you to write simple (pytest) assertions to test whether collections have the same content, regardless of order. For example:

assert [1, 20, 300] == unordered([20, 300, 1])

It is especially useful when testing APIs that return some complex data structures in an arbitrary order, e.g.:

assert response.json() == {
    "people": unordered(
        # Here we test that the collection type is list
        [
            {
                "name": "Alice",
                "age": 20,
                "children": unordered(
                    # Here the collection type is not important
                    {"name": "Bob", "age": 2}, 
                    {"name": "Carol", "age": 3},
                ),
            },
            {
                "name": "Dave",
                "age": 30,
                "children": unordered(
                    {"name": "Eve", "age": 5}, 
                    {"name": "Frank", "age": 6},
                ),
            },
        ]
    ),
}

Installation

pip install pytest-unordered

Usage

Basics

In most cases you just need the unordered() helper function:

from pytest_unordered import unordered

Compare list or tuples by wrapping your expected value with unordered():

assert [1, 20, 300] == unordered([20, 300, 1])  # Pass
assert (1, 20, 300) == unordered((20, 300, 1))  # Pass

Excessive/missing items will be reported by pytest:

assert [1, 20, 300] == unordered([20, 300, 1, 300])

  E         Extra items in the right sequence:
  E         300

By default, the container type has to match too:

assert (1, 20, 300) == unordered([20, 300, 1])

  E         Type mismatch:
  E         <class 'tuple'> != <class 'list'>

Nesting

A seasoned developer will notice that the simple use cases above can also be addressed with appropriate usage of builtins like set(), sorted(), isinstance(), repr(), etc, but these solutions scale badly (in terms of boilerplate code) with the complexity of your data structures. For example: naively implementing order ignoring comparison with set() or sorted() does not work with lists of dictionaries because dictionaries are not hashable or sortable. unordered() supports this out of the box however:

assert [{"bb": 20}, {"a": 1}] == unordered([{"a": 1}, {"bb": 20}])  # Pass

The true value of unordered() lies in the fact that you can apply it inside large nested data structures to skip order checking only in desired places with surgical precision and without a lot of boilerplate code. For example:

expected = unordered([
    {"customer": "Alice", "orders": unordered([123, 456])},
    {"customer": "Bob", "orders": [789, 1000]},
])

actual = [
    {"customer": "Bob", "orders": [789, 1000]},
    {"customer": "Alice", "orders": [456, 123]},
]

assert actual == expected

In this example we wrapped the outer customer list and the order list of Alice with unordered(), but didn't wrap Bob's order list. With the actual value of above (where customer order is different and Alice's orders are reversed), the assertion will pass. But if the orders of Bob would be swapped in actual, the assertion will fail and pytest will report:

E         Differing items:
E         {'orders': [1000, 789]} != {'orders': [789, 1000]}

Container type checking

As noted, the container types should be (by default) equal to pass the assertion. If you don't want this type check, call unordered() in a variable argument fashion (instead of passing a container as single argument):

assert [1, 20, 300] == unordered(20, 300, 1)  # Pass
assert (1, 20, 300) == unordered(20, 300, 1)  # Pass

This pattern also allows comparing with iterators, generators and alike:

assert iter([1, 20, 300]) == unordered(20, 300, 1)  # Pass
assert unordered(i for i in range(3)) == [2, 1, 0]  # Pass

If you want to enforce type checking when passing a single generator expression, pass check_type=True:

assert unordered((i for i in range(3)), check_type=True) == [2, 1, 0]  # Fail
assert unordered((i for i in range(3)), check_type=True) == (i for i in range(2, -1, -1))  # Pass

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_unordered-0.6.1.tar.gz (7.2 kB view details)

Uploaded Source

Built Distribution

pytest_unordered-0.6.1-py3-none-any.whl (6.0 kB view details)

Uploaded Python 3

File details

Details for the file pytest_unordered-0.6.1.tar.gz.

File metadata

  • Download URL: pytest_unordered-0.6.1.tar.gz
  • Upload date:
  • Size: 7.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.8.19

File hashes

Hashes for pytest_unordered-0.6.1.tar.gz
Algorithm Hash digest
SHA256 061f7a538247f8adc97a4fcf7415d36e0db4b16548c42d5b49168e6ec2cd95b0
MD5 9d59c935a012bd37e97925c5ee82de9e
BLAKE2b-256 a08f85275d036f702a5af3b24a5e6460df3a5d3ae8ffae3ed2625fb4cae97f84

See more details on using hashes here.

File details

Details for the file pytest_unordered-0.6.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_unordered-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 baa809a0ff811d97cfd85f138dbca52e2d7831612b4e19225b3a65ebd9fce068
MD5 9d1431463c4d82dcb53edc8bbb94dbf1
BLAKE2b-256 c265aae0ad8a7f4cc3e7117ac69ccd81cbd0bc90192485a2f60327c263c22344

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page