Source code for fedora_messaging.testing

# SPDX-FileCopyrightText: 2024 Red Hat, Inc
#
# SPDX-License-Identifier: GPL-2.0-or-later

"""
Once you've written code to publish or consume messages, you'll probably want
to test it. The :mod:`fedora_messaging.testing` module has utilities for common
test patterns.

If you find yourself implementing a pattern over and over in your test code,
consider contributing it here!
"""

import inspect
from collections.abc import Generator
from contextlib import contextmanager
from typing import Union
from unittest import mock

from .message import Message


[docs] @contextmanager def mock_sends(*expected_messages: Union[Message, type[Message]]) -> Generator[list[Message]]: """ Assert a block of code results in the provided messages being sent without actually sending them. This is intended for unit tests. The call to publish is mocked out and messages are captured and checked at the end of the ``with``. For example: >>> from fedora_messaging import api, testing >>> def publishes(): ... api.publish(api.Message(body={"Hello": "world"})) ... >>> with testing.mock_sends(api.Message, api.Message(body={"Hello": "world"})): ... publishes() ... publishes() ... >>> with testing.mock_sends(api.Message(body={"Goodbye": "everybody"})): ... publishes() ... AssertionError Args: *expected_messages: The messages you expect to be sent. These can be classes instances of classes derived from :class:`fedora_messaging.message.Message`. If the class is provided, the message is checked to make sure it is an instance of that class and that it passes schema validation. If an instance is provided, it is checked for equality with the sent message. Raises: AssertionError: If the messages published don't match the messages asserted. """ sent: list[Message] = [] with mock.patch("fedora_messaging.api.crochet"): with mock.patch("fedora_messaging.api._twisted_publish_wrapper") as mock_pub: yield sent messages = [call[0][0] for call in mock_pub.call_args_list] sent.extend(messages) if len(expected_messages) != len(messages): raise AssertionError( f"Expected {len(expected_messages)} messages to be sent, but {len(messages)} were sent" ) for msg, expected in zip(messages, expected_messages): if inspect.isclass(expected): if not isinstance(msg, expected): raise AssertionError( f"Expected message of type {expected}, but {msg.__class__} was sent" ) else: assert msg.topic == expected.topic assert msg.body == expected.body assert msg == expected msg.validate()