Dramatiq actors run inline with StubBroker and results middleware: working versions (tested 2026-10-15)
Define Dramatiq actors that store results, run them synchronously against a StubBroker with a background Worker, and assert on results, failures and async actors offline.
passed5 passed · 0 failed2026-10-15 15:58 UTC- Status
- passed
- Last passed
- 2026-10-15 15:58 UTC
- Tests
- 5 / 5
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions3
dramatiq@2.2.1pytest@9.1.1pytest-asyncio@1.4.0
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38764699834
The code
Exactly the files that passed. Your agent can pull them with get_recipe("dramatiq-stub-broker-testing") (install), or see them on GitHub.
requirements.txt
dramatiq==2.2.1 pytest==9.1.1 pytest-asyncio==1.4.0
tasks.py
import asyncio
import dramatiq
from dramatiq.brokers.stub import StubBroker
from dramatiq.middleware.asyncio import AsyncIO
from dramatiq.results import Results
from dramatiq.results.backends.stub import StubBackend
broker = StubBroker()
broker.add_middleware(AsyncIO())
broker.add_middleware(Results(backend=StubBackend()))
dramatiq.set_broker(broker)
@dramatiq.actor(store_results=True)
def add(x, y):
return x + y
@dramatiq.actor
def square(x):
return x * x
@dramatiq.actor(store_results=True, max_retries=0)
def divide(x, y):
return x / y
@dramatiq.actor(store_results=True)
async def upper(text):
await asyncio.sleep(0)
return text.upper()
tests/test_tasks.py
import asyncio
import pytest
from dramatiq.results.errors import ResultFailure, ResultMissing
from dramatiq.worker import Worker
from tasks import add, broker, divide, square, upper
@pytest.fixture
def worker():
w = Worker(broker, worker_timeout=100, worker_threads=1)
w.start()
yield w
w.stop()
broker.dead_letters_by_queue.clear()
def test_actor_runs_on_worker(worker):
message = add.send(2, 3)
broker.join(add.queue_name)
assert message.get_result(backend=broker.get_results_backend()) == 5
def test_result_missing_without_store_results(worker):
message = square.send(4)
broker.join(square.queue_name)
with pytest.raises(ResultMissing):
message.get_result(backend=broker.get_results_backend())
def test_join_fail_fast_reraises_actor_error(worker):
divide.send(1, 0)
with pytest.raises(ZeroDivisionError):
broker.join(divide.queue_name, fail_fast=True)
def test_result_failure_after_non_fail_fast_join(worker):
message = divide.send(1, 0)
broker.join(divide.queue_name, fail_fast=False)
with pytest.raises(ResultFailure) as exc:
message.get_result(backend=broker.get_results_backend(), block=True)
assert "ZeroDivisionError" in str(exc.value)
async def test_async_actor_result_is_retrieved_from_a_thread(worker):
message = upper.send("abc")
await asyncio.to_thread(broker.join, upper.queue_name)
result = await asyncio.to_thread(message.get_result, backend=broker.get_results_backend(), block=True)
assert result == "ABC"
pytest.ini
[pytest] pythonpath = . asyncio_mode = auto
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.