← Files TemporalARCHIVED FILE
skills/temporal-developer/references/python/testing.md
5.42 KB · Nov 9, 2026 · 00:16 UTC
# Python SDK Testing
## Overview
You test Temporal Python Workflows using the Temporal testing package plus a normal Python test framework like pytest. The Temporal Python SDK provides `WorkflowEnvironment` for testing workflows in a local environment and `ActivityEnvironment` for isolated activity testing.
## Workflow Test Environment
The core pattern is:
1. Start a test WorkflowEnvironment (`WorkflowEnvironment.start_local()`).
2. Start a Worker in that environment with your Workflow and Activities registered.
3. Use the environment’s client to execute the Workflow, using a fresh UUID for the task queue name and workflow ID.
4. Assert on the result or status.
`WorkflowEnvironment.start_local` configures a ready-to-go local environment for running and testing workflows:
```python
import uuid
import pytest
from temporalio.testing import WorkflowEnvironment
from temporalio.worker import Worker
from activities import my_activity
from workflows import MyWorkflow
@pytest.mark.asyncio
async def test_workflow():
task_queue_name = str(uuid.uuid4())
async with await WorkflowEnvironment.start_local() as env:
async with Worker(
env.client,
task_queue=task_queue_name,
workflows=[MyWorkflow],
activities=[my_activity],
):
result = await env.client.execute_workflow(
MyWorkflow.run,
"input",
id=str(uuid.uuid4()),
task_queue=task_queue_name,
)
```
Conveniently, the local `env` can be shared among tests, e.g. via a pytest fixture.
If your workflows / tests involve long durations (such as using Temporal timers / sleeps), then you can use the time-skipping environment, via `WorkflowEnvironment.start_time_skipping()`.
Only use time-skipping if you must. It can *not* be shared among tests.
## Mocking Activities
```python
import uuid
import pytest
from temporalio import activity
from temporalio.testing import WorkflowEnvironment
from temporalio.worker import Worker
from workflows import MyWorkflow
@activity.defn(name="compose_greeting")
async def compose_greeting_mocked(input: str) -> str:
return "mocked result"
@pytest.mark.asyncio
async def test_with_mock():
task_queue_name = str(uuid.uuid4())
async with await WorkflowEnvironment.start_local() as env:
async with Worker(
env.client,
task_queue=task_queue_name,
workflows=[MyWorkflow],
activities=[compose_greeting_mocked],
):
result = await env.client.execute_workflow(...)
```
## Testing Signals and Queries
```python
@pytest.mark.asyncio
async def test_signals():
async with await WorkflowEnvironment.start_local() as env:
async with Worker(...):
handle = await env.client.start_workflow(...) # same arguments as to execute_workflow
# Send signal
await handle.signal(MyWorkflow.my_signal, "data")
# Query state
status = await handle.query(MyWorkflow.get_status)
assert status == "expected"
# Wait for completion
result = await handle.result()
```
## Testing Failure Cases
Below shows an example of how to test failure cases:
```python
# Test failure scenarios
@pytest.mark.asyncio
async def test_activity_failure_handling():
async with await WorkflowEnvironment.start_local() as env:
# An example activity that always fails
@activity.defn
async def failing_activity() -> str:
raise ApplicationError("Simulated failure", non_retryable=True)
async with Worker(...):
with pytest.raises(WorkflowFailureError):
await env.client.execute_workflow(...)
```
## Workflow Replay Testing
For replay tests that read history from a JSON file, use the [CLI export command](../core/cli-workflow-commands.md#workflow-show) to create that file.
For worker integration tests that should replay workflow history on every Workflow Task, configure the worker with `max_cached_workflows=0`. This complements replaying saved histories with `Replayer`.
To make nondeterminism fail the Workflow Execution immediately in a test, pass `workflow_failure_exception_types=[workflow.NondeterminismError]` to `Worker` (with `from temporalio import workflow`).
```python
import json
import pytest
import uuid
from temporalio.client import WorkflowHistory
from temporalio.worker import Replayer
from workflows import MyWorkflow
@pytest.mark.asyncio
async def test_replay():
with open("example-history.json", "r") as f:
history_json = json.load(f)
replayer = Replayer(workflows=[MyWorkflow])
# From JSON file
await replayer.replay_workflow(
WorkflowHistory.from_json(str(uuid.uuid4()), history_json)
)
```
## Activity Testing
```python
import pytest
from temporalio.testing import ActivityEnvironment
@pytest.mark.asyncio
async def test_activity():
env = ActivityEnvironment()
result = await env.run(my_activity, "arg1", "arg2")
assert result == "expected"
```
## Best Practices
1. Use the `WorkflowEnvironment.start_local` environment for most testing
2. Use time-skipping environment for workflows with durable timers / durable sleeps.
3. Mock external dependencies in activities
4. Test replay compatibility, especially when changing workflow code
5. Test signal/query handlers explicitly
6. Use unique workflow IDs and task queues per test to avoid conflicts. Easiest is a `uuid.uuid4()`
SHA-256: 1746e02bb41ee567428af4b7a4c1bf11d2e3e1c6ff62c575f616977e813966f2