Skip to content

Testing API Reference

Reference for the test runners, the test result, operation accessors, and the enums used across the testing SDKs. For an onboarding walkthrough, start with Authoring.

LocalDurableTestRunner

Runs a durable handler in-process against an in-memory checkpoint store. No AWS credentials, no deployment, and no containers are required. Use it for unit tests and CI.

Create a runner

new LocalDurableTestRunner({ handlerFunction: handler })

Call LocalDurableTestRunner.setupTestEnvironment() in beforeAll and LocalDurableTestRunner.teardownTestEnvironment() in afterAll.

static setupTestEnvironment(params?: LocalDurableTestRunnerSetupParameters): Promise<void>
static teardownTestEnvironment(): Promise<void>

Constructor parameters:

  • handlerFunction (required) The handler created with withDurableExecution.
DurableFunctionTestRunner(handler=handler, poll_interval=1.0)

Use as a context manager. The context manager starts the scheduler thread on entry and stops it on exit.

Parameters:

  • handler (required) The handler decorated with @durable_execution.
  • poll_interval (optional) Seconds between internal polling cycles. Defaults to 1.0.
// Class-based input type
LocalDurableTestRunner.create(Class<I> inputType, BiFunction<I, DurableContext, O> handlerFn)

// TypeToken-based input type (for generic types)
LocalDurableTestRunner.create(TypeToken<I> inputType, BiFunction<I, DurableContext, O> handlerFn)

// With a custom DurableConfig
LocalDurableTestRunner.create(Class<I> inputType, BiFunction<I, DurableContext, O> handlerFn, DurableConfig config)

// From a DurableHandler instance (extracts config automatically)
LocalDurableTestRunner.create(Class<I> inputType, DurableHandler<I, O> handler)

Override config or the output type on an existing runner:

LocalDurableTestRunner<I, O> withDurableConfig(DurableConfig config)
LocalDurableTestRunner<I, O> withOutputType(Class<O> outputType)
LocalDurableTestRunner<I, O> withOutputType(TypeToken<O> outputType)

Parameters:

  • inputType (required) The input type class or TypeToken.
  • handlerFn (required) A BiFunction<I, DurableContext, O> implementing the handler logic.
  • config (optional) A DurableConfig. The runner overrides the DurableExecutionClient with its in-memory implementation but preserves all other settings such as custom SerDes.
  • handler (optional) A DurableHandler instance. The runner extracts its configuration automatically.

Run the handler

run(params?: InvokeRequest): Promise<TestResult>

run() drives the full replay loop until the execution completes or fails.

Parameters:

  • params (optional) An InvokeRequest object.
    • payload (optional) The input payload to pass to the handler.

Returns: Promise<TestResult>

run(
    input: str | None = None,
    timeout: int = 900,
    function_name: str = "test-function",
    execution_name: str = "execution-name",
    account_id: str = "123456789012",
) -> DurableFunctionTestResult

Parameters:

  • input (optional) JSON-encoded input string. Defaults to None.
  • timeout (optional) Maximum seconds to wait for completion. Defaults to 900.
  • function_name (optional) Function name used internally. Defaults to "test-function".
  • execution_name (optional) Execution name used internally. Defaults to "execution-name".
  • account_id (optional) Account ID used internally. Defaults to "123456789012".

Returns: DurableFunctionTestResult

Raises: TimeoutError if the execution does not complete within timeout.

// Runs a single invocation (may return PENDING)
TestResult<O> run(I input)

// Drives the full replay loop until SUCCEEDED, FAILED, or PENDING with no
// auto-advanceable operations
TestResult<O> runUntilComplete(I input)

Parameters:

  • input (required) The handler input.

Returns: TestResult<O>

Run asynchronously

Not applicable. run() already drives the full replay loop.

# Start execution without waiting for completion
run_async(
    input: str | None = None,
    timeout: int = 900,
    function_name: str = "test-function",
    execution_name: str = "execution-name",
    account_id: str = "123456789012",
) -> str  # returns execution_arn

# Wait for a running execution to complete
wait_for_result(execution_arn: str, timeout: int = 60) -> DurableFunctionTestResult

Not applicable on the local runner. run() runs a single invocation, runUntilComplete() drives the full loop. The cloud runner exposes startAsync(), see CloudDurableTestRunner.

Inspect operations

getOperation(name: string, index?: number): DurableOperation
getOperationByIndex(index: number): DurableOperation
getOperationByNameAndIndex(name: string, index: number): DurableOperation
getOperationById(id: string): DurableOperation

Parameters:

  • name (required) The operation name.
  • index (optional) Zero-based index when multiple operations share the same name. Defaults to 0.
  • id (required) The unique operation ID.

Returns: DurableOperation

Not available on the runner. Inspect operations through the result: result.get_step(name), result.get_wait(name), result.get_callback(name), result.get_context(name), result.get_invoke(name), result.get_operation_by_name(name), and result.get_all_operations(). See TestResult.

TestOperation getOperation(String name)

Parameters:

  • name (required) The operation name.

Returns: TestOperation, or null if not found.

Drive callbacks

Callback interaction is on the DurableOperation object. Get a handle with getOperation() and then call waitForData() and sendCallback*() on it. See Drive a callback from an operation.

# Wait for a callback to become available and return its ID
wait_for_callback(execution_arn: str, name: str | None = None, timeout: int = 60) -> str

# Send a successful callback result
send_callback_success(callback_id: str, result: bytes | None = None) -> None

# Send a callback failure
send_callback_failure(callback_id: str, error: ErrorObject | None = None) -> None

# Send a callback heartbeat
send_callback_heartbeat(callback_id: str) -> None
// Get the callback ID for a named callback operation
String getCallbackId(String operationName)

// Complete a callback with a success result
void completeCallback(String callbackId, String result)

// Fail a callback
void failCallback(String callbackId, ErrorObject error)

// Time out a callback
void timeoutCallback(String callbackId)

Drive chained invokes

Not applicable. Register a mock handler for the invoked function and let the test drive the real handler path. See Register mock handlers for invoke.

Not applicable.

// Complete a chained invoke with a success result
void completeChainedInvoke(String name, String result)

// Fail a chained invoke
void failChainedInvoke(String name, ErrorObject error)

// Time out a chained invoke
void timeoutChainedInvoke(String name)

// Stop a chained invoke
void stopChainedInvoke(String name, ErrorObject error)

Register mock handlers for invoke

// Register a durable function for context.invoke() calls
registerDurableFunction(functionName: string, handler: DurableLambdaHandler): this

// Register a standard Lambda function for context.invoke() calls
registerFunction(functionName: string, handler: Handler): this

Both methods return the runner for chaining.

Not applicable.

Not applicable.

Simulate failures

Not applicable.

Not applicable.

// Reset a step checkpoint to STARTED (simulates checkpoint failure)
void resetCheckpointToStarted(String stepName)

// Remove a step checkpoint (simulates fire-and-forget loss)
void simulateFireAndForgetCheckpointLoss(String stepName)

Control time

Pass { skipTime: true } to setupTestEnvironment(). Both context.wait() delays and step retry delays complete in zero wall-clock time.

Set the DURABLE_EXECUTION_TIME_SCALE environment variable to a float that multiplies context.wait() durations. Step retry delays use the configured next_attempt_delay_seconds at real wall-clock time and the scale does not apply to them.

void advanceTime()

runUntilComplete() calls advanceTime() after each invocation. When you call run() directly, call advanceTime() yourself between invocations. advanceTime() marks PENDING step retries as READY and completes STARTED waits without real-time sleeps.

See Authoring: Skip time in tests for an overview.

Reset between runs

reset(): void

Clears the operation index, wait manager, and operation storage so the runner can be reused across tests.

Create a new DurableFunctionTestRunner instance per test.

Create a new LocalDurableTestRunner instance per test.

Reference types

LocalDurableTestRunnerSetupParameters

interface LocalDurableTestRunnerSetupParameters {
  skipTime?: boolean;
  checkpointDelay?: number;
}

Fields:

  • skipTime (optional) Install fake timers so retry delays and waits complete instantly. Defaults to false.
  • checkpointDelay (optional) Simulated delay in milliseconds on checkpoint API calls. Useful for surfacing race conditions.

Not applicable. Configure with the DURABLE_EXECUTION_TIME_SCALE environment variable and the poll_interval constructor argument.

Not applicable. Configure with withDurableConfig, withOutputType, and advanceTime() on the runner instance.

TestResult

The object returned by run(). Exposes the execution status, the final result, any error, and the full operation history.

Status

getStatus(): ExecutionStatus | undefined

Returns: ExecutionStatus (SUCCEEDED, FAILED, PENDING, or undefined).

result.status  # InvocationStatus

Type: InvocationStatus (SUCCEEDED, FAILED, PENDING, TIMED_OUT, STOPPED).

ExecutionStatus getStatus()
boolean isSucceeded()
boolean isFailed()

Returns: ExecutionStatus (SUCCEEDED, FAILED, PENDING).

Result

getResult(): TResult | undefined

Returns: The deserialized execution output, or undefined if not available.

Throws: The execution error if the execution failed.

result.result  # str | None

The raw JSON-encoded result payload.

<T> T getResult(Class<T> resultType)
<T> T getResult(TypeToken<T> resultType)
O getResult()  // requires withOutputType() to be set

Returns: The deserialized execution output.

Throws: IllegalStateException if the execution did not succeed.

Error

getError(): TestResultError

Returns: A TestResultError object.

Throws: If the execution succeeded.

result.error  # ErrorObject | None
Optional<ErrorObject> getError()

Operations

getOperations(params?: { status: OperationStatus }): DurableOperation[]

Parameters:

  • params (optional) Filter by OperationStatus.

Returns: Array of DurableOperation.

result.operations               # list[Operation], top-level only
result.get_all_operations()     # list[Operation], including nested

# Typed lookup by name
result.get_step(name: str) -> StepOperation
result.get_wait(name: str) -> WaitOperation
result.get_context(name: str) -> ContextOperation
result.get_callback(name: str) -> CallbackOperation
result.get_invoke(name: str) -> InvokeOperation
result.get_execution(name: str) -> ExecutionOperation
result.get_operation_by_name(name: str) -> Operation

Raises: DurableFunctionsTestError if the operation is not found.

List<TestOperation> getOperations()
List<TestOperation> getSucceededOperations()
List<TestOperation> getFailedOperations()
TestOperation getOperation(String name)

getOperation(name) returns null if not found.

History events

getHistoryEvents(): Event[]

Not available on the test result.

List<Event> getHistoryEvents()
List<Event> getEventsForOperation(String operationName)

Invocations

getInvocations(): Invocation[]

Useful for asserting on the number of Lambda re-invocations the test runner drove.

Not available on the test result.

Not available on the test result.

Pretty-print

print(config?: { parentId?: boolean; name?: boolean; type?: boolean; ... }): void

Writes a formatted table of operations to stdout.

Not applicable.

Not applicable.

Reference types

TestResultError

interface TestResultError {
  errorMessage: string | undefined;
  errorType: string | undefined;
  errorData: string | undefined;
  stackTrace: string[] | undefined;
}

Uses ErrorObject from the main SDK:

@dataclass
class ErrorObject:
    error_message: str | None
    error_type: str | None

Uses ErrorObject from software.amazon.awssdk.services.lambda.model:

ErrorObject.errorMessage()  // String
ErrorObject.errorType()     // String

Operation

Represents a single entry in the operation history. Each type of operation exposes its own details block (step, wait, callback, chained invoke, context, execution) on top of a shared set of accessors.

Common accessors

getName(): string | undefined
getType(): OperationType | undefined
getSubType(): OperationSubType | undefined
getStatus(): OperationStatus | undefined
getStartTimestamp(): Date | undefined
getEndTimestamp(): Date | undefined
getId(): string | undefined
getParentId(): string | undefined
getOperationData(): Operation | undefined
getEvents(): Event[] | undefined
getChildOperations(): DurableOperation[] | undefined
isWaitForCallback(): boolean
isCallback(): boolean

Every operation type inherits these fields from the base Operation dataclass:

operation.operation_id: str
operation.operation_type: OperationType
operation.status: OperationStatus
operation.name: str | None
operation.parent_id: str | None
operation.sub_type: OperationSubType | None
operation.start_timestamp: datetime | None
operation.end_timestamp: datetime | None
String getName()
OperationType getType()
String getSubtype()
OperationStatus getStatus()
Duration getDuration()
boolean isCompleted()
List<Event> getEvents()
String getId()

Step details

getStepDetails(): StepDetails | undefined
step.result: OperationPayload | None
step.error: ErrorObject | None
step.attempt: int
step.next_attempt_timestamp: datetime | None
step.child_operations: list[Operation]
StepDetails getStepDetails()

// Convenience deserialization for step results
<T> T getStepResult(Class<T> type)
<T> T getStepResult(TypeToken<T> type)

// Step error
ErrorObject getError()

// Retry attempt number (0-based)
int getAttempt()

Wait details

getWaitDetails(): WaitResultDetails | undefined

WaitResultDetails exposes waitSeconds and scheduledEndTimestamp.

wait.scheduled_end_timestamp: datetime | None
WaitDetails getWaitDetails()

WaitDetails.scheduledEndTimestamp() returns an Instant.

Callback details

getCallbackDetails(): CallbackDetails | undefined
callback.callback_id: str | None
callback.result: OperationPayload | None
callback.error: ErrorObject | None
callback.child_operations: list[Operation]
CallbackDetails getCallbackDetails()

Chained invoke details

getChainedInvokeDetails(): ChainedInvokeDetails | undefined
invoke.result: OperationPayload | None
invoke.error: ErrorObject | None
ChainedInvokeDetails getChainedInvokeDetails()

Context details

getContextDetails(): ContextDetails | undefined
ctx.result: OperationPayload | None
ctx.error: ErrorObject | None
ctx.child_operations: list[Operation]

# Typed lookup among the context's children
ctx.get_step(name) -> StepOperation
ctx.get_wait(name) -> WaitOperation
ctx.get_context(name) -> ContextOperation
ctx.get_callback(name) -> CallbackOperation
ctx.get_invoke(name) -> InvokeOperation
ContextDetails getContextDetails()

Execution details

Not applicable.

Not applicable.

ExecutionDetails getExecutionDetails()

Drive a callback from an operation

waitForData(status?: WaitingOperationStatus): Promise<DurableOperation>
sendCallbackSuccess(result?: string): Promise<SendDurableExecutionCallbackSuccessResponse>
sendCallbackFailure(error?: ErrorObject): Promise<SendDurableExecutionCallbackFailureResponse>
sendCallbackHeartbeat(): Promise<SendDurableExecutionCallbackHeartbeatResponse>

Driven from the runner. See LocalDurableTestRunner: Drive callbacks.

Driven from the runner. See LocalDurableTestRunner: Drive callbacks.

Enums

Execution status

The terminal status of a durable execution.

ExecutionStatus from @aws-sdk/client-lambda:

Value Meaning
SUCCEEDED Execution completed successfully
FAILED Execution failed
PENDING Execution is waiting (callback, invoke, etc.)
TIMED_OUT Execution exceeded its timeout
STOPPED Execution was stopped

InvocationStatus from aws_durable_execution_sdk_python.execution:

Value Meaning
SUCCEEDED Execution completed successfully
FAILED Execution failed
PENDING Execution is waiting
TIMED_OUT Execution exceeded its timeout
STOPPED Execution was stopped

ExecutionStatus from software.amazon.lambda.durable.model:

Value Meaning
SUCCEEDED Execution completed successfully
FAILED Execution failed
PENDING Execution is waiting

Operation status

The status of an individual operation.

OperationStatus from @aws-sdk/client-lambda:

Value Meaning
STARTED Operation is running
SUCCEEDED Operation completed successfully
FAILED Operation failed
PENDING Operation is queued
CANCELLED Operation was cancelled
TIMED_OUT Operation exceeded its timeout
STOPPED Operation was stopped

OperationStatus from aws_durable_execution_sdk_python.lambda_service. Same values as TypeScript.

OperationStatus from software.amazon.awssdk.services.lambda.model. Same values as TypeScript.

Operation type

OperationType from @aws-sdk/client-lambda:

Value Meaning
STEP A step operation
WAIT A wait operation
CALLBACK A callback operation
CHAINED_INVOKE An invoke operation
CONTEXT A child context
EXECUTION The root execution operation

OperationType from aws_durable_execution_sdk_python.lambda_service. Same values as TypeScript.

OperationType from software.amazon.awssdk.services.lambda.model. Same values as TypeScript.

Waiting operation status

WaitingOperationStatus from @aws/durable-execution-sdk-js-testing:

Value Meaning
STARTED Fires when the operation starts
SUBMITTED For callbacks: fires when the submitter function completes. For other operations: same as COMPLETED
COMPLETED Fires when the operation reaches a terminal status

Not applicable. Use runner.wait_for_callback() to wait for a callback to become available.

Not applicable. Use runner.getCallbackId() after run() returns PENDING.

CloudDurableTestRunner

Invokes a deployed Lambda function, polls for completion, and retrieves the full operation history for assertions. Use it to validate deployment, IAM permissions, and real service integrations. Both runners share the same TestResult and Operation types, so tests written against the local runner run unchanged against the cloud runner.

Create a runner

new CloudDurableTestRunner({
  functionName: string,
  client?: LambdaClient,
  config?: CloudDurableTestRunnerConfig,
})

Parameters:

  • functionName (required) The function name or ARN. A qualifier is required for durable functions.
  • client (optional) A configured LambdaClient. Defaults to new LambdaClient().
  • config (optional) A CloudDurableTestRunnerConfig object.
DurableFunctionCloudTestRunner(
    function_name: str,
    region: str = "us-west-2",
    lambda_endpoint: str | None = None,
    poll_interval: float = 1.0,
)

Parameters:

  • function_name (required) The function name or ARN.
  • region (optional) AWS region. Defaults to "us-west-2".
  • lambda_endpoint (optional) Custom Lambda endpoint URL. Defaults to None.
  • poll_interval (optional) Seconds between status polls. Defaults to 1.0.
// Class-based types
CloudDurableTestRunner.create(String functionArn, Class<I> inputType, Class<O> outputType)

// TypeToken-based types
CloudDurableTestRunner.create(String functionArn, TypeToken<I> inputType, TypeToken<O> outputType)

// With custom LambdaClient
CloudDurableTestRunner.create(String functionArn, Class<I> inputType, Class<O> outputType, LambdaClient lambdaClient)

Override settings on an existing runner:

CloudDurableTestRunner<I, O> withLambdaClient(LambdaClient lambdaClient)
CloudDurableTestRunner<I, O> withPollInterval(Duration interval)
CloudDurableTestRunner<I, O> withTimeout(Duration timeout)
CloudDurableTestRunner<I, O> withInvocationType(InvocationType type)
CloudDurableTestRunner<I, O> withSerDes(SerDes serDes)

Parameters:

  • functionArn (required) The function ARN. A qualifier is required for durable functions.
  • inputType (required) The input type class or TypeToken.
  • outputType (required) The output type class or TypeToken.
  • lambdaClient (optional) A configured LambdaClient. Defaults to a client using DefaultCredentialsProvider.

Run the handler

run(params?: InvokeRequest): Promise<TestResult>

Parameters:

  • params (optional) An InvokeRequest object.
    • payload (optional) The input payload.

Returns: Promise<TestResult>

run(input: str | None = None, timeout: int = 60) -> DurableFunctionTestResult

Parameters:

  • input (optional) JSON-encoded input string.
  • timeout (optional) Maximum seconds to wait. Defaults to 60.

Returns: DurableFunctionTestResult

Raises: TimeoutError if the execution does not complete within timeout.

TestResult<O> run(I input)
TestResult<O> runUntilComplete(I input)

Parameters:

  • input (required) The handler input.

Returns: TestResult<O>

Run asynchronously

Not applicable. run() already drives the full replay loop.

# Start execution without waiting
run_async(input: str | None = None, timeout: int = 60) -> str  # returns execution_arn

# Wait for a running execution to complete
wait_for_result(execution_arn: str, timeout: int = 60) -> DurableFunctionTestResult
AsyncExecution<O> startAsync(I input)

The returned AsyncExecution<O> exposes pollUntil(), pollUntilComplete(), isComplete(), hasOperation(), hasCallback(), getCallbackId(), getOperation(), getOperations(), getStatus(), getExecutionArn(), completeCallback(), failCallback(), and heartbeatCallback().

Inspect operations

getOperation(name: string): DurableOperation
getOperationByIndex(index: number): DurableOperation
getOperationByNameAndIndex(name: string, index: number): DurableOperation
getOperationById(id: string): DurableOperation

Inspect operations through the test result. See TestResult.

TestOperation getOperation(String name)

Drive callbacks

Callback interaction is on the DurableOperation object. See Drive a callback from an operation.

wait_for_callback(execution_arn: str, name: str | None = None, timeout: int = 60) -> str
send_callback_success(callback_id: str, result: bytes | None = None) -> None
send_callback_failure(callback_id: str, error: ErrorObject | None = None) -> None
send_callback_heartbeat(callback_id: str) -> None

Drive callbacks through the AsyncExecution<O> returned by startAsync(). Use getCallbackId(), completeCallback(), failCallback(), and heartbeatCallback() on the async handle.

Configure polling and timeouts

Pass config: { pollInterval, invocationType } to the constructor. See CloudDurableTestRunnerConfig.

Pass poll_interval to the constructor. Pass timeout to run() or run_async() to set the maximum wait.

Use withPollInterval(Duration), withTimeout(Duration), and withInvocationType(InvocationType) on the runner.

Reset between runs

reset(): void

Create a new runner instance per test.

Create a new runner instance per test.

Reference types

CloudDurableTestRunnerConfig

interface CloudDurableTestRunnerConfig {
  pollInterval?: number;
  invocationType?: InvocationType;
}

Fields:

  • pollInterval (optional) Milliseconds between history polls. Defaults to 1000.
  • invocationType (optional) Lambda invocation type. Defaults to InvocationType.RequestResponse.

Not a separate config object. Pass poll_interval directly to the constructor.

Not a separate config object. Use the with* builder methods on the runner.

See also

  • Authoring Set up the local runner and write your first test.
  • Assertions Inspect steps, waits, and callbacks after a test run.
  • Workflow patterns Complete tests for common workflow shapes.
  • Cloud Runner Run tests against a deployed Lambda function.
  • Runner How the replay loop and checkpointing work.