Skip to content
LlamaAgents
Agent Workflows

Testing Workflows

Workflows are easiest to test as event-driven systems. Run the workflow, collect the events it streamed, assert the final result, and inspect the context state when state matters.

The workflows.testing module includes a small runner for this:

from workflows.testing import WorkflowTestRunner

WorkflowTestRunner starts the workflow, drains its event stream, awaits the final result, and returns everything in one object:

import pytest
from workflows import Context, Workflow, step
from workflows.events import Event, StartEvent, StopEvent
from workflows.testing import WorkflowTestRunner
class Progress(Event):
message: str
class Done(Event):
value: str
class ExampleWorkflow(Workflow):
@step
async def start(self, ctx: Context, ev: StartEvent) -> Done:
ctx.write_event_to_stream(Progress(message="started"))
return Done(value=ev.topic.upper())
@step
async def finish(self, ev: Done) -> StopEvent:
return StopEvent(result=ev.value)
@pytest.mark.asyncio
async def test_workflow_streams_progress_and_returns_result() -> None:
result = await WorkflowTestRunner(ExampleWorkflow()).run(
start_event=StartEvent(topic="docs")
)
assert result.result == "DOCS"
assert result.event_types[Progress] == 1
assert any(
isinstance(ev, Progress) and ev.message == "started"
for ev in result.collected
)

The returned object has:

FieldMeaning
resultThe final value returned by awaiting the workflow handler.
collectedEvery streamed event that was not excluded.
event_typesA count of collected events by event class.
ctxThe final Context, useful for state assertions or snapshots.

By default the runner exposes internal events, including StepStateChanged. That is useful when you want to assert execution shape:

from workflows.events import StepStateChanged
@pytest.mark.asyncio
async def test_step_state_events_are_emitted() -> None:
result = await WorkflowTestRunner(ExampleWorkflow()).run(
start_event=StartEvent(topic="docs")
)
assert result.event_types[StepStateChanged] > 0

If a test only cares about user events, turn internal events off:

result = await WorkflowTestRunner(ExampleWorkflow()).run(
start_event=StartEvent(topic="docs"),
expose_internal=False,
)

Or keep internal events available but exclude the noisy ones from the collected list:

result = await WorkflowTestRunner(ExampleWorkflow()).run(
start_event=StartEvent(topic="docs"),
exclude_events=[StepStateChanged]
)

Use the returned context when a workflow writes to ctx.store:

from pydantic import BaseModel, Field
class CounterState(BaseModel):
count: int = Field(default=0)
class CounterWorkflow(Workflow):
@step
async def count(self, ctx: Context[CounterState], ev: StartEvent) -> StopEvent:
async with ctx.store.edit_state() as state:
state.count += 1
return StopEvent(result="done")
@pytest.mark.asyncio
async def test_workflow_updates_state() -> None:
result = await WorkflowTestRunner(CounterWorkflow()).run()
state = await result.ctx.store.get_state()
assert state.count == 1

For durable workflow code, prefer asserting behavior after a real snapshot and restore:

workflow = CounterWorkflow()
first = await WorkflowTestRunner(workflow).run()
ctx_dict = first.ctx.to_dict()
restored_workflow = CounterWorkflow()
restored = Context.from_dict(restored_workflow, ctx_dict)
resumed = await WorkflowTestRunner(restored_workflow).run(ctx=restored)

That catches the mistakes unit tests often miss: state that cannot serialize, events that are not importable when restored, and side effects that are not safe to repeat.

Note for AI agents: this documentation is built for programmatic access. - Overview of all docs: https://developers.llamaindex.ai/llms.txt - Any page is available as raw Markdown by appending index.md to its URL — e.g. https://developers.llamaindex.ai/llamaparse/parse/getting_started/index.md - Agent-friendly REST search APIs live under https://developers.llamaindex.ai/api/ — search (BM25 full-text), grep (regex), read (fetch a page), and list (browse the doc tree). See https://developers.llamaindex.ai/llms.txt for parameters. - A hosted documentation MCP server is available at https://developers.llamaindex.ai/mcp. If you support MCP, you can ask the user to install it for browsing these docs directly (an alternative to the REST API). Setup: https://developers.llamaindex.ai/python/shared/mcp/