Skip to content

feat: add ExecuteStandaloneActivity to TestWorkflowEnvironment#2337

Closed
brucearctor wants to merge 1 commit into
temporalio:mainfrom
brucearctor:feat/test-standalone-activities
Closed

feat: add ExecuteStandaloneActivity to TestWorkflowEnvironment#2337
brucearctor wants to merge 1 commit into
temporalio:mainfrom
brucearctor:feat/test-standalone-activities

Conversation

@brucearctor
Copy link
Copy Markdown
Contributor

What

Adds ExecuteStandaloneActivity to TestWorkflowEnvironment that executes standalone activities through the full ClientOutboundInterceptor chain.

Closes #2318

Why

Currently, TestActivityEnvironment.ExecuteActivity() runs activity functions directly via taskHandler.Execute(), completely bypassing the ClientOutboundInterceptor chain. This prevents testing of interceptor-dependent features like OpenTelemetry tracing (#2302), metrics, and header propagation without spinning up a real server.

This method fills that gap by matching the real Client.ExecuteActivity flow through the interceptor chain while keeping execution local to the test environment.

How

  • testClientOutboundInterceptor: a terminal ClientOutboundInterceptor that replaces gRPC calls with the existing test activity task handler execution
  • testClientActivityHandleImpl: implements ClientActivityHandle for the test environment
  • executeStandaloneActivity: builds the interceptor chain from registered WorkerOptions.Interceptors (type-asserting WorkerInterceptor to ClientInterceptor) and routes execution through it

The flow is: contextWithNewHeader(ctx) → build interceptor chain → chain.ExecuteActivity() → user interceptors → testClientOutboundInterceptor.ExecuteActivity()taskHandler.Execute().

Design Decisions

There were a few decision points during implementation. I went with what seemed most natural given the existing codebase patterns, but am happy to revisit any of these if the maintainers prefer a different approach:

  1. Method lives on TestWorkflowEnvironment (not a new TestClientEnvironment) — The issue requested it on TestWorkflowEnvironment, and it keeps the API surface minimal. If a separate TestClientEnvironment is preferred for cleaner separation, I can refactor.

  2. Interceptor chain built per-call — Rather than caching the chain, each call to ExecuteStandaloneActivity rebuilds it. This matches how the real client initializes its chain and avoids stale state if SetWorkerOptions is called between invocations. If caching is preferred for performance, that would be straightforward to add.

  3. Interceptors sourced from WorkerOptions.Interceptors — Since the test environment uses SetWorkerOptions to configure interceptors, I type-assert each WorkerInterceptor to ClientInterceptor when building the chain. This works for the common case where users pass full Interceptor implementations (which embed both). If a separate SetClientInterceptors option is preferred, I can add that.

  4. Synchronous execution with result on the handle — Activities execute synchronously (matching existing test suite behavior), so handle.Get() returns immediately. Cancel, Terminate, and Describe return errors indicating they are unsupported in the test environment.

Tests

Added 6 tests covering:

  • Basic execution and result retrieval
  • Default options (auto-generated ID, default task queue and timeouts)
  • Activity failure propagation
  • Interceptor chain traversal — verifies user interceptor ExecuteActivity is called and headers are present
  • Unregistered activity (expects panic, matching existing behavior)
  • Custom data converter

@brucearctor brucearctor requested a review from a team as a code owner May 14, 2026 03:24
Add ExecuteStandaloneActivity method to TestWorkflowEnvironment that
executes standalone activities through the full ClientOutboundInterceptor
chain, enabling testing of interceptor-dependent features such as
OpenTelemetry tracing, metrics, and header propagation.

Unlike TestActivityEnvironment.ExecuteActivity (which runs the activity
function directly via taskHandler.Execute), this method builds and
traverses the client interceptor chain before executing the activity,
matching the real Client.ExecuteActivity flow.

Implementation:
- testClientOutboundInterceptor: terminal interceptor replacing gRPC
  calls with local test execution
- testClientActivityHandleImpl: implements ClientActivityHandle for
  the test environment
- executeStandaloneActivity: builds interceptor chain from registered
  WorkerOptions.Interceptors and routes through it

Closes temporalio#2318
@brucearctor brucearctor force-pushed the feat/test-standalone-activities branch from 97d9880 to 54ec32c Compare May 14, 2026 03:30
@yuandrew
Copy link
Copy Markdown
Contributor

yuandrew commented May 20, 2026

Thanks for the contribution, but we've decided the issue itself is not worth the effort and not quite what we want and have closed, so closing this contribution as well

@yuandrew yuandrew closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Standalone Activities to TestWorkflowEnvironment

2 participants