Skip to content

Expectations#

An expectation describes the desired content of an object. It can be any class, as long as there is an asserter registered for it in the used asserter factory.

Out-of-the-Box Expectations#

The module comes with several generic asserters that support the following expectation types:

  1. Built-in Classes:

    • Mapping (from collections.abc)
    • list or tuple
    • set
  2. Special Expectation Classes (importable from panda_pytest_assertions.assert_object):

    • Stringified
    • IsType
    • Is
    • Unordered
    • MappingSubset
    • ObjectAttributes
    • WithType

Additionally, any object can be used as an expectation, and it will be compared using the == operator to the object. This fallback mechanism is used if the expectation does not match any of the specific supported types.

Any Object#

Any object can be used as an expectation. This is especially useful for primitive types, but it can also be applied to anything that implements a meaningful __eq__ method. The assertion passes if the asserted object equals the expectation (assert expectation == object_).

Python
1
2
3
4
assert_object(
    expectation=True,
    object_=1,
)

This asserter should always be placed at the end of the factory registry, as it matches any kind of object and will shadow other asserters otherwise.

Stringified#

The simplest expectation is the Stringified expectation. While not very useful by itself, it becomes valuable when building complex expectations. It asserts that the stringified value of the asserted object (str(object_)) is equal to a given string.

Example usage:

Python
1
2
3
4
assert_object(
    expectation=Stringified('2020-06-08 12:30:00'),
    object_=datetime(2020, 6, 8, 12, 30),
)

IsType#

The IsType expectation is used to assert any object. It is initialized with an expected type name, and optionally an expected type module fully qualified name. For the assertion to succeed:

  • The asserted object's type must be named according to the expectation.
  • It's type must be defined in the expected module (if set in expectation).

Example usage:

Python
1
2
3
4
5
6
7
assert_object(
    expectation=IsType(
        'Munch',
        'munch',
    ),
    object_=Munch(color='brown', legs=4),
)

Notes:

  • The asserted object can be of any type.
  • The name of the asserted object's type is extracted using object_.__class__.__name__.
  • The name of a module of the asserted object's type is extracted using object_.__class__.__module__.
  • If the expected module name is not set (is set to None), the check for the module name is not performed.

The IsType class provides the helper function is_type, which allows for easier setting of the expected type and module name. The function accepts 1 or 2 positional parameters and returns the IsType object.

Examples:

Python
1
2
3
is_type(some_type)
is_type('some_type_name')
is_type('some_type_name', 'some_module_name')

The expected type name and module name will be set according to the rules described below.

  • If a single parameter is passed and it is a type, the values will be extracted from this type.
  • If a single parameter is passed and it is a string it will be used as an expected type name and the expected module name will be left unset.
  • In case two string parameters are passed, they will be used as expected type name and expected module name, respectively.

The above example can be rewritten as follows:

Python
1
2
3
4
assert_object(
    expectation=is_type(Munch),
    object_=Munch(color='brown', legs=4),
)

Is#

The Is expectation is used to assert any object. It is initialized with any object. For the assertion to succeed:

  • The asserted object's type must have the same identity as the one passed to the expectation.

Example usage:

Python
1
2
3
4
assert_object(
    expectation=Is(object_ := Munch(color='brown', legs=4)),
    object_=object_,
)

Notes:

  • The asserted object can be of any type.
  • The identity is compared using is operator.

Mapping#

The Mapping expectation is used to assert mappings (e.g., dictionaries). It defines a mapping between keys and expectations for the values assigned to those keys. For the assertion to succeed:

  • The asserted object must contain the exact same keys as the expectation (no more, no less).
  • The values assigned to specific keys must fulfill the expectations defined for those keys in the mapping expectation.

Example usage:

Python
assert_object(
    expectation={
        2: {'env': 'water', 'fur': False},
        1: Stringified("{'color': 'brown', 'legs': 4}"),
    },
    object_={
        1: {'color': 'brown', 'legs': 4},
        2: {'fur': False, 'env': 'water'},
    },
)

Notes:

  • The asserted object must be an instance of collections.abc.Mapping.
  • Although dictionaries in Python are ordered, the order of keys in mappings does not matter for this type of expectation.
  • Any supported expectation type can be used as the value in the mapping expectation.
  • Keys of the expectation and asserted object are extracted for comparison using .keys().
  • Keys and values are extracted from the expectation using .items().
  • Values are extracted from the object using object_[key_from_expectation].

list or tuple#

The list or tuple expectation is used to assert iterables. It consists of ordered elements' expectations. For the assertion to succeed:

  • The asserted object must contain the same number of elements as the expectation.
  • The element at index i in the asserted object must fulfill the expectation's element at the same index.

Example usage:

Python
assert_object(
    expectation=[
        {'env': 'water', 'fur': False},
        Stringified("{'color': 'brown', 'legs': 4}"),
    ],
    object_=[
        {'fur': False, 'env': 'water'},
        {'color': 'brown', 'legs': 4},
    ],
)

assert_object(
    expectation=['a', 'b', 'c'],
    object_='abc',  # string is iterable
)

Notes:

  • The asserted object must be an instance of collections.abc.Iterable.
  • The order of elements matters.
  • Any supported expectation type can be used as an element in this expectation.

set or Unordered#

The set or Unordered expectation is used to assert iterables. It is either a set of expectations or an Unordered object created from a list of expectations. For the assertion to succeed:

  • The asserted object must contain the same number of elements as the expectation.
  • Each element in the asserted object must fulfill one unique expectation.

Example usage:

Python
assert_object(
    expectation={'water', 'fire', 'earth', 'air'},
    object_=[
        'fire',
        'earth',
        'water',
        'air',
    ],
)

assert_object(
    expectation={'c', 'a', 'b'},
    object_='abc',  # string is iterable
)

assert_object(
    expectation=Unordered([Stringified('4'), 3, 2]),
    object_=[2, 4, 3],
)

Notes:

  • The asserted object must be an instance of collections.abc.Iterable.
  • Any supported expectation type can be used as an element in this expectation.
  • The comparison logic processes all elements of the expectation in the order they are returned (which is not guaranteed by the set!). It tries to find the first element in the asserted object that fulfills the expectation. If successful, the expectation and the matched element are no longer considered. Be cautious when expectations match multiple elements; the order matters.

Example with potential behavior change due to order:

Python
1
2
3
4
assert_object(
    expectation=Unordered([Stringified('1'), 1]),
    object_=[1, '1']
)

The first expectation matches the first element of an object (str(1) == '1'), so the asserter is unable to find a match for the second expectation, as 1 != '1'. If the order of expectations in the list were changed, the assertion would pass.

MappingSubset#

The MappingSubset expectation is used to assert mappings (e.g., dictionaries). It is initialized with a mapping between keys and expectations for the values assigned to those keys. For the assertion to succeed:

  • The asserted object must contain at least the same keys as the expectation (but can contain more).
  • The values assigned to specific keys must fulfill the expectations defined for those keys in the mapping expectation.

Example usage:

Python
assert_object(
    expectation=MappingSubset(
        {
            2: {'env': 'water', 'fur': False},
            1: Stringified("{'color': 'brown', 'legs': 4}"),
        },
    ),
    object_={
        1: {'color': 'brown', 'legs': 4},
        3: 'anything',
        2: {'fur': False, 'env': 'water'},
    },
)

Notes:

  • The asserted object must be an instance of collections.abc.Mapping.
  • Although dictionaries in Python are ordered, the order of keys in mappings does not matter for this type of expectation.
  • Any supported expectation type can be used as the value in the mapping expectation.
  • Keys of the expectation and asserted object are extracted for comparison using .keys().
  • Keys and values are extracted from the expectation using .items().
  • Values are extracted from the object using object_[key_from_expectation].

ObjectAttributes#

The ObjectAttributes expectation is used to assert any object. It is initialized with a mapping between attribute names and expectations for their values. For the assertion to succeed:

  • The asserted object must contain at least the attributes defined in the expectation (but can contain more).
  • The values assigned to specific attributes must fulfill the expectations defined for those attribute names in the mapping expectation.

Example usage:

Python
assert_object(
    expectation=ObjectAttributes(
        {
            'abc': {'env': 'water', 'fur': False},
            'xyz': Stringified("{'color': 'brown', 'legs': 4}"),
        },
    ),
    object_=Munch(
        xyz={'color': 'brown', 'legs': 4},
        another='anything',
        abc={'fur': False, 'env': 'water'},
    ),
)

Notes:

  • The asserted object can be of any type.
  • The order of keys in mappings does not matter.
  • Keys and values are extracted from the expectation using .items().
  • Any supported expectation type can be used as the value in the main expectation.
  • Checking whether an attribute exists in an object is done using the hasattr function.
  • Values for attributes are extracted from the object using getattr(object_, key_from_expectation).

WithType#

The WithType expectation is used to assert any object. It is initialized with an inner expectation, an expected type name, and optionally an expected type module fully qualified name. For the assertion to succeed:

  • The asserted object must fulfill the inner expectation.
  • Its type must be named according to the expectation.
  • The type must be defined in the expected module (if set in the main expectation).

Example usage:

Python
1
2
3
4
5
6
7
8
assert_object(
    expectation=WithType(
        Stringified("Munch({'color': 'brown', 'legs': 4})"),
        'Munch',
        'munch',
    ),
    object_=Munch(color='brown', legs=4),
)

Notes:

  • The asserted object can be of any type.
  • The name of the asserted object's type is extracted using object_.__class__.__name__.
  • The name of a module of the asserted object's type is extracted using object_.__class__.__module__.
  • If the expected module name is not set (is set to None), the check for the module name is not performed.

The WithType class provides the helper function with_type, which allows for easier setting of the expected type and module name. The function accepts from 0 to 2 positional parameters and returns another function that creates the WithType object.

Examples:

Python
1
2
3
4
with_type()(some_expectation)
with_type(some_type)(some_expectation)
with_type('some_type_name')(some_expectation)
with_type('some_type_name', 'some_module_name')(some_expectation)

In all cases, the inner expectation of WithType will be set to some_expectation, and the expected type name and module name will be set according to the rules described below.

  • If no parameters are provided, the values for the expected type name and module name will be extracted from some_expectation.
  • If a single parameter is passed and it is a type, the values will be extracted from this type.
  • If a single parameter is passed and it is a string it will be used as an expected type name and the expected module name will be left unset.
  • In case two string parameters are passed, they will be used as expected type name and expected module name, respectively.

The above example can be rewritten as follows:

Python
1
2
3
4
assert_object(
    expectation=with_type(Munch)(Stringified("Munch({'color': 'brown', 'legs': 4})")),
    object_=Munch(color='brown', legs=4),
)