Skip to content

Testing

test_harness runs the mediators the application already uses and records what they did. It does not add a bus. Commands and queries stay cqrs.Request. Import it from cqrs.testing. It is not in cqrs.__all__.

Two entries return the same harness. Building a stand takes the same mapper and middleware arguments as bootstrap. The builder passes compensation_retry_delay=0 unless the test passes a delay. around does not change the delay of a mediator that is already built.

from cqrs.testing import test_harness

async with test_harness(
    container,
    commands_mapper=commands_mapper,
    queries_mapper=queries_mapper,
    domain_events_mapper=domain_events_mapper,
    events_mapper=events_mapper,
    streaming_mapper=streaming_mapper,
    sagas_mapper=sagas_mapper,
    middlewares=middlewares,
    scope_strategy=cqrs.ScopeStrategy.NONE,
) as harness:
    await harness.mediator.send(CreateOrder(sku="book"))

around wraps mediators the application already built. For the block it wraps resolve, the broker, saga storage, and the listener, and puts them back in finally.

async with test_harness.around(
    mediator,
    event_mediator=event_mediator,
    streaming_mediator=streaming_mediator,
    saga_mediator=saga_mediator,
    outbox=outbox,
    inbox=inbox,
) as harness:
    await mediator.send(CreateOrder(sku="book"))

test_harness(...) creates a memory outbox and a memory inbox and stores them on harness.outbox_repository and harness.inbox_repository. It does not bind those ports. The test binds the port.

TestContainer is register(type, instance) and async resolve. For ScopeStrategy.SEND, pass di.Container, not TestContainer.

If the mediator holds DevnullMessageBroker, the harness replaces it with CapturingMessageBroker for the block and restores it afterwards. Do not read DevnullMessageBroker.MESSAGE_BUS.

contains is a boolean. assert_contains, assert_one, and assert_empty raise AssertionError and list what was recorded. There is no pytest plugin. The same calls work under unittest.

An invocation record has handler, method (handle, act, or compensate), argument, argument_type, and handler_instance.

Journals

Three delivery channels stay separate. A test that asks published for a domain event gets a miss.

  • invocations records entry into handle, act, and compensate: the class, the method, and the argument. A chain step, a fallback, a saga step, and an EventMediator handler are here. EventMediator goes through EventDispatcher, not EventEmitter, so the emitter listener does not see it.
  • handled is a DomainEvent that EventEmitter gave to handlers after a command, a stream, or a saga step.
  • published is a message that reached MessageBroker.send_message. That is a direct NotificationEvent, and later EventProducer.
  • outbox is a row the handler stored. It is not in published until produce_available.
  • requests is each send: the request type, and the response or the exception. A command and a query are both cqrs.Request.
  • yields is what a stream yielded, in order.
  • middlewares records which middleware saw the request and whether it called handle.
  • handler_errors is a domain-handler error that send swallows and logs.
  • unhandled is a domain event whose map is empty.
  • sagas is status, context, and step history from storage.
  • inbox is insert, a duplicate, and processing through InboxProcessor.

assert harness.published.contains(OrderCreated) is false for a domain event. assert_contains says the type is on handled or invocations and did not go to the broker.

Handlers

  • Command

    Assert the command on the requests and invocations journals.

    Read More

  • Query

    After a read, the handled, published, and outbox journals are empty.

    Read More

  • Domain event

    Assert the domain event on the handled and invocations journals.

    Read More

  • Notification

    Assert the notice on the published journal, including its topic.

    Read More

  • Parallel handlers

    Assert both handler classes on the invocations journal.

    Read More

  • Handler errors

    The command result returns from send; the error is on the handler_errors journal.

    Read More

  • Unhandled event

    Assert the event type on the unhandled journal.

    Read More

  • Event mediator

    Assert the handler on the invocations journal, method handle.

    Read More

  • Request fallback

    Assert both classes on invocations; the requests journal holds the fallback response.

    Read More

  • Event fallback

    Assert both classes on the invocations journal.

    Read More

  • Circuit breaker

    An open fake ICircuitBreaker leaves the primary off the invocations journal.

    Read More

  • Middleware

    The middlewares journal records called_next; requests holds the middleware response.

    Read More

  • Scope SEND

    Assert invocation order on the invocations journal under ScopeStrategy.SEND.

    Read More

  • Scope HANDLER and NONE

    Assert both handlers on the invocations journal; order is not checked.

    Read More

  • Chain

    Assert the entered step on the invocations journal.

    Read More

  • Streaming

    Assert values on the yields journal and the event on handled.

    Read More

  • Streaming fallback

    Assert both handlers on the yields and invocations journals.

    Read More

  • Saga step

    Assert act on the invocations journal and the running saga count.

    Read More

  • Saga compensation

    Assert compensate on the invocations journal and the failed saga status.

    Read More

  • Saga fallback

    Assert both classes on the invocations journal; the saga stays completed.

    Read More

  • Saga circuit breaker

    A fake ISagaStepCircuitBreaker leaves the primary off the invocations journal.

    Read More

  • Saga recovery

    Recover through the sagas journal; a finished step does not act again.

    Read More

  • Inbox

    Assert the command on the requests journal and the processed inbox row.

    Read More

  • Inbox failure

    Assert NOT_PROCESSED and flush_counter on the inbox journal.

    Read More

  • Outbox

    Assert the row on the outbox journal, then the notice on published.

    Read More

  • Outbox broker error

    A broker error leaves the outbox row not produced; the published journal stays empty.

    Read More

Models

  • Dataclass

    The response is on requests. The harness adds no validator.

    Read More

  • Pydantic

    ValidationError is raised by the constructor, before send. A valid send returns the response.

    Read More

  • JSON

    Default path for a published message. content_type is None.

    Read More

  • Protobuf

    Published message content_type and payload_bytes. The harness does not import protobuf.

    Read More

  • di

    ScopeStrategy.SEND on di.Container shares one session. Pass di.Container, not TestContainer.

    Read More

Recipes

  • FastAPI

    TestClient and dependency_overrides. Assert requests and outbox. The harness does not import FastAPI.

    Read More

  • FastStream

    TestKafkaBroker delivers. The harness checks invocations or the inbox and does not import FastStream.

    Read More

  • SQLAlchemy

    Session rollback and SqlAlchemyOutboxedEventRepository on SQLite. The harness does not import SQLAlchemy.

    Read More

  • SQLModel

    SqlModelOutboxedEventRepository under the sqlmodel marker. The harness does not import SQLModel.

    Read More

  • dishka

    around wraps a mediator that is already built. The harness does not import dishka.

    Read More

  • dependency-injector

    around wraps the mediator. The adapter does not implement SupportsScope, so scope_strategy is not SEND.

    Read More

  • Kafka and AMQP

    The unit test uses CapturingMessageBroker. Real Kafka and AMQP adapters stay in integration tests.

    Read More

Outside the harness

These pages are here so the feature is not dropped. They are not a test_harness run.

  • Mermaid

    CoRMermaid and SagaMermaid draw sequence and class diagrams. They take no part in send.

    Read More

  • Compressor

    ZlibCompressor is checked by the serializer tests. The harness sees payload_bytes as finished bytes.

    Read More

  • Alembic

    rebind is a migration, not a handler run. The fence is the short form.

    Read More

  • Transaction log tailing

    A process that reads the database log. It is not mediator.send and is not run through the harness.

    Read More