Skip to main content

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

Release files for pytest-unordered 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-unordered 0.8.0
File Size Uploaded
pytest_unordered-0.8.0.tar.gz 8.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-unordered 0.8.0
File Interpreter ABI Platform
pytest_unordered-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size:14.3 kB

Release files / pytest_unordered-0.8.0.tar.gz

Download URL pytest_unordered-0.8.0.tar.gz
Size 8.0 kB
Tags Source
SHA-256 checksum
How to use checksums
3c369ed86919d3eb35e11fd27bb679c1e3506ead9327e25aa8a07307256be65a
BLAKE2b-256 checksum
How to use checksums
cb0cab409d508c92ea6737875cfc39a696a0e0bd44359915e1376831dfb5a456
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.13

Release files / pytest_unordered-0.8.0-py3-none-any.whl

Download URL pytest_unordered-0.8.0-py3-none-any.whl
Size 6.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c792b879032b33141205e1a38a80234eb0117474bf9459ae65557e29837c2a1c
BLAKE2b-256 checksum
How to use checksums
ddd42f63db7b6292856f51a76e3eb0acb5e53fd63ae6b6ec4dd11af24985f5b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page