• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

temporalio / sdk-java / #400

06 Oct 2026 02:56AM UTC coverage: 68.324% (+0.007%) from 68.317%
#400

push

github

web-flow
Mark dev server APIs as stable (#3112)

7902 of 13718 branches covered (57.6%)

Branch coverage included in aggregate %.

32093 of 44819 relevant lines covered (71.61%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.49
/temporal-testing/src/main/java/io/temporal/testing/TestWorkflowExtension.java
1
package io.temporal.testing;
2

3
import static io.temporal.testing.internal.TestServiceUtils.applyNexusServiceOptions;
4

5
import com.uber.m3.tally.Scope;
6
import io.temporal.api.enums.v1.IndexedValueType;
7
import io.temporal.api.nexus.v1.Endpoint;
8
import io.temporal.client.ActivityClient;
9
import io.temporal.client.ActivityClientOptions;
10
import io.temporal.client.WorkflowClient;
11
import io.temporal.client.WorkflowClientOptions;
12
import io.temporal.client.WorkflowOptions;
13
import io.temporal.common.metadata.POJOWorkflowImplMetadata;
14
import io.temporal.common.metadata.POJOWorkflowInterfaceMetadata;
15
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
16
import io.temporal.worker.Worker;
17
import io.temporal.worker.WorkerFactoryOptions;
18
import io.temporal.worker.WorkerOptions;
19
import io.temporal.worker.WorkflowImplementationOptions;
20
import io.temporal.workflow.DynamicWorkflow;
21
import java.lang.reflect.Constructor;
22
import java.lang.reflect.Parameter;
23
import java.time.Instant;
24
import java.util.*;
25
import javax.annotation.Nonnull;
26
import org.junit.jupiter.api.extension.AfterEachCallback;
27
import org.junit.jupiter.api.extension.BeforeEachCallback;
28
import org.junit.jupiter.api.extension.ExtensionContext;
29
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
30
import org.junit.jupiter.api.extension.ParameterContext;
31
import org.junit.jupiter.api.extension.ParameterResolutionException;
32
import org.junit.jupiter.api.extension.ParameterResolver;
33
import org.junit.jupiter.api.extension.TestWatcher;
34
import org.junit.platform.commons.support.AnnotationSupport;
35

36
/**
37
 * JUnit Jupiter extension that simplifies testing of Temporal workflows.
38
 *
39
 * <p>The extension manages Temporal test environment and workflow worker lifecycle, and be used
40
 * with both in-memory (default) and standalone temporal service (see {@link
41
 * Builder#useInternalService()}, {@link Builder#useExternalService()} and {@link
42
 * Builder#useExternalService(String)}}).
43
 *
44
 * <p>This extension can inject workflow stubs as well as instances of {@link
45
 * TestWorkflowEnvironment}, {@link WorkflowClient}, {@link ActivityClient}, {@link
46
 * WorkflowOptions}, {@link Worker}, into test methods.
47
 *
48
 * <p>Usage example:
49
 *
50
 * <pre><code>
51
 * public class MyTest {
52
 *
53
 *  {@literal @}RegisterExtension
54
 *   public static final TestWorkflowExtension workflowExtension =
55
 *       TestWorkflowExtension.newBuilder()
56
 *           .setWorkflowTypes(MyWorkflowImpl.class)
57
 *           .setActivityImplementations(new MyActivities())
58
 *           .build();
59
 *
60
 *  {@literal @}Test
61
 *   public void testMyWorkflow(MyWorkflow workflow) {
62
 *     // Test code that calls MyWorkflow methods
63
 *   }
64
 * }
65
 * </code></pre>
66
 */
67
public class TestWorkflowExtension
68
    implements ParameterResolver, TestWatcher, BeforeEachCallback, AfterEachCallback {
69

70
  private enum ServiceType {
1 ✔
71
    IN_MEMORY,
1 ✔
72
    EXTERNAL,
1 ✔
73
    DEV_SERVER
1 ✔
74
  }
75

76
  private static final String TEST_ENVIRONMENT_KEY = "testEnvironment";
77
  private static final String WORKER_KEY = "worker";
78
  private static final String WORKFLOW_OPTIONS_KEY = "workflowOptions";
79
  private static final String NEXUS_ENDPOINT_KEY = "nexusEndpoint";
80

81
  private final WorkerOptions workerOptions;
82
  private final WorkflowClientOptions workflowClientOptions;
83
  private final ActivityClientOptions activityClientOptions;
84
  private final WorkerFactoryOptions workerFactoryOptions;
85
  private final Map<Class<?>, WorkflowImplementationOptions> workflowTypes;
86
  private final Object[] activityImplementations;
87
  private final Object[] nexusServiceImplementations;
88
  private final ServiceType serviceType;
89
  private final TemporalDevServerOptions devServerOptions;
90
  private final String target;
91
  private final boolean doNotStart;
92
  private final boolean doNotSetupNexusEndpoint;
93
  private final long initialTimeMillis;
94
  private final boolean useTimeskipping;
95
  @Nonnull private final Map<String, IndexedValueType> searchAttributes;
96
  private final Scope metricsScope;
97

98
  private final Set<Class<?>> supportedParameterTypes = new HashSet<>();
1 ✔
99
  private boolean includesDynamicWorkflow;
100

101
  private TestWorkflowExtension(Builder builder) {
1 ✔
102
    workerOptions = builder.workerOptions;
1 ✔
103
    if (builder.workflowClientOptions != null) {
1 !
104
      workflowClientOptions = builder.workflowClientOptions;
×
105
    } else {
106
      workflowClientOptions =
1 ✔
107
          WorkflowClientOptions.newBuilder().setNamespace(builder.namespace).build();
1 ✔
108
    }
109
    activityClientOptions = builder.activityClientOptions;
1 ✔
110
    workerFactoryOptions = builder.workerFactoryOptions;
1 ✔
111
    workflowTypes = builder.workflowTypes;
1 ✔
112
    activityImplementations = builder.activityImplementations;
1 ✔
113
    nexusServiceImplementations = builder.nexusServiceImplementations;
1 ✔
114
    serviceType = builder.serviceType;
1 ✔
115
    devServerOptions = builder.devServerOptions;
1 ✔
116
    target = builder.target;
1 ✔
117
    doNotStart = builder.doNotStart;
1 ✔
118
    doNotSetupNexusEndpoint = builder.doNotSetupNexusEndpoint;
1 ✔
119
    initialTimeMillis = builder.initialTimeMillis;
1 ✔
120
    useTimeskipping = builder.useTimeskipping;
1 ✔
121
    this.searchAttributes = builder.searchAttributes;
1 ✔
122
    this.metricsScope = builder.metricsScope;
1 ✔
123

124
    supportedParameterTypes.add(TestWorkflowEnvironment.class);
1 ✔
125
    supportedParameterTypes.add(WorkflowClient.class);
1 ✔
126
    supportedParameterTypes.add(ActivityClient.class);
1 ✔
127
    supportedParameterTypes.add(WorkflowOptions.class);
1 ✔
128
    supportedParameterTypes.add(Worker.class);
1 ✔
129

130
    for (Class<?> workflowType : workflowTypes.keySet()) {
1 ✔
131
      if (DynamicWorkflow.class.isAssignableFrom(workflowType)) {
1 ✔
132
        includesDynamicWorkflow = true;
1 ✔
133
        continue;
1 ✔
134
      }
135
      POJOWorkflowImplMetadata metadata = POJOWorkflowImplMetadata.newInstance(workflowType);
1 ✔
136
      for (POJOWorkflowInterfaceMetadata workflowInterface : metadata.getWorkflowInterfaces()) {
1 ✔
137
        supportedParameterTypes.add(workflowInterface.getInterfaceClass());
1 ✔
138
      }
1 ✔
139
    }
1 ✔
140
  }
1 ✔
141

142
  public static Builder newBuilder() {
143
    return new Builder();
1 ✔
144
  }
145

146
  @Override
147
  public boolean supportsParameter(
148
      ParameterContext parameterContext, ExtensionContext extensionContext)
149
      throws ParameterResolutionException {
150

151
    Parameter parameter = parameterContext.getParameter();
1 ✔
152
    if (parameter.getDeclaringExecutable() instanceof Constructor) {
1 !
153
      // Constructor injection is not supported
154
      return false;
×
155
    }
156

157
    Class<?> parameterType = parameter.getType();
1 ✔
158
    if (supportedParameterTypes.contains(parameterType)) {
1 ✔
159
      return true;
1 ✔
160
    }
161

162
    if (!includesDynamicWorkflow) {
1 !
163
      // If no DynamicWorkflow implementation was registered then supportedParameterTypes are the
164
      // only ones types that can be injected
165
      return false;
×
166
    }
167

168
    try {
169
      // If POJOWorkflowInterfaceMetadata can be instantiated then parameterType is a proper
170
      // workflow interface and can be injected
171
      POJOWorkflowInterfaceMetadata.newInstance(parameterType);
1 ✔
172
      return true;
1 ✔
173
    } catch (Exception e) {
×
174
      return false;
×
175
    }
176
  }
177

178
  @Override
179
  public Object resolveParameter(
180
      ParameterContext parameterContext, ExtensionContext extensionContext)
181
      throws ParameterResolutionException {
182

183
    Class<?> parameterType = parameterContext.getParameter().getType();
1 ✔
184
    if (parameterType == TestWorkflowEnvironment.class) {
1 ✔
185
      return getTestEnvironment(extensionContext);
1 ✔
186
    } else if (parameterType == WorkflowClient.class) {
1 ✔
187
      return getTestEnvironment(extensionContext).getWorkflowClient();
1 ✔
188
    } else if (parameterType == ActivityClient.class) {
1 ✔
189
      return getTestEnvironment(extensionContext).getActivityClient();
1 ✔
190
    } else if (parameterType == WorkflowOptions.class) {
1 ✔
191
      return getWorkflowOptions(extensionContext);
1 ✔
192
    } else if (parameterType == Worker.class) {
1 ✔
193
      return getWorker(extensionContext);
1 ✔
194
    } else {
195
      // Workflow stub
196
      return getTestEnvironment(extensionContext)
1 ✔
197
          .getWorkflowClient()
1 ✔
198
          .newWorkflowStub(parameterType, getWorkflowOptions(extensionContext));
1 ✔
199
    }
200
  }
201

202
  @Override
203
  public void beforeEach(ExtensionContext context) {
204
    long currentInitialTimeMillis =
1 ✔
205
        AnnotationSupport.findAnnotation(context.getElement(), WorkflowInitialTime.class)
1 ✔
206
            .map(annotation -> Instant.parse(annotation.value()).toEpochMilli())
1 ✔
207
            .orElse(initialTimeMillis);
1 ✔
208

209
    TestEnvironmentOptions testEnvironmentOptions = createTestEnvOptions(currentInitialTimeMillis);
1 ✔
210
    TestWorkflowEnvironment testEnvironment =
211
        serviceType == ServiceType.DEV_SERVER
1 !
212
            ? TestWorkflowEnvironment.startLocal(testEnvironmentOptions, devServerOptions)
×
213
            : TestWorkflowEnvironment.newInstance(testEnvironmentOptions);
1 ✔
214

215
    try {
216
      String taskQueue =
1 ✔
217
          String.format("WorkflowTest-%s-%s", context.getDisplayName(), context.getUniqueId());
1 ✔
218
      String nexusEndpointName = String.format("WorkflowTestNexusEndpoint-%s", UUID.randomUUID());
1 ✔
219
      boolean createNexusEndpoint =
1 !
220
          !doNotSetupNexusEndpoint && nexusServiceImplementations.length > 0;
221
      Worker worker = testEnvironment.newWorker(taskQueue, workerOptions);
1 ✔
222
      workflowTypes.forEach(
1 ✔
223
          (wft, o) -> {
224
            if (createNexusEndpoint) {
1 ✔
225
              o = applyNexusServiceOptions(o, nexusServiceImplementations, nexusEndpointName);
1 ✔
226
            }
227
            worker.registerWorkflowImplementationTypes(o, wft);
1 ✔
228
          });
1 ✔
229
      worker.registerActivitiesImplementations(activityImplementations);
1 ✔
230
      worker.registerNexusServiceImplementation(nexusServiceImplementations);
1 ✔
231

232
      if (!doNotStart) {
1 !
233
        testEnvironment.start();
1 ✔
234
      }
235
      if (createNexusEndpoint) {
1 ✔
236
        setNexusEndpoint(
1 ✔
237
            context, testEnvironment.createNexusEndpoint(nexusEndpointName, taskQueue));
1 ✔
238
      }
239

240
      setTestEnvironment(context, testEnvironment);
1 ✔
241
      setWorker(context, worker);
1 ✔
242
      setWorkflowOptions(context, WorkflowOptions.newBuilder().setTaskQueue(taskQueue).build());
1 ✔
243
    } catch (RuntimeException | Error failure) {
×
244
      try {
245
        testEnvironment.close();
×
246
      } catch (RuntimeException | Error cleanupFailure) {
×
247
        failure.addSuppressed(cleanupFailure);
×
248
      }
×
249
      throw failure;
×
250
    }
1 ✔
251
  }
1 ✔
252

253
  protected TestEnvironmentOptions createTestEnvOptions(long initialTimeMillis) {
254
    return TestEnvironmentOptions.newBuilder()
1 ✔
255
        .setWorkflowClientOptions(workflowClientOptions)
1 ✔
256
        .setActivityClientOptions(activityClientOptions)
1 ✔
257
        .setWorkerFactoryOptions(workerFactoryOptions)
1 !
258
        .setUseExternalService(serviceType == ServiceType.EXTERNAL)
1 ✔
259
        .setUseTimeskipping(useTimeskipping)
1 ✔
260
        .setTarget(target)
1 ✔
261
        .setInitialTimeMillis(initialTimeMillis)
1 ✔
262
        .setMetricsScope(metricsScope)
1 ✔
263
        .setSearchAttributes(searchAttributes)
1 ✔
264
        .build();
1 ✔
265
  }
266

267
  @Override
268
  public void afterEach(ExtensionContext context) {
269
    Endpoint endpoint = getNexusEndpoint(context);
1 ✔
270
    TestWorkflowEnvironment testEnvironment = getTestEnvironment(context);
1 ✔
271
    if (endpoint != null && !testEnvironment.getOperatorServiceStubs().isShutdown()) {
1 !
272
      testEnvironment.deleteNexusEndpoint(endpoint);
1 ✔
273
    }
274
    testEnvironment.close();
1 ✔
275
  }
1 ✔
276

277
  @Override
278
  public void testFailed(ExtensionContext context, Throwable cause) {
279
    if (serviceType == ServiceType.IN_MEMORY) {
×
280
      TestWorkflowEnvironment testEnvironment = getTestEnvironment(context);
×
281
      System.err.println("Workflow execution histories:\n" + testEnvironment.getDiagnostics());
×
282
    }
283
  }
×
284

285
  private TestWorkflowEnvironment getTestEnvironment(ExtensionContext context) {
286
    return getStore(context).get(TEST_ENVIRONMENT_KEY, TestWorkflowEnvironment.class);
1 ✔
287
  }
288

289
  private void setTestEnvironment(
290
      ExtensionContext context, TestWorkflowEnvironment testEnvironment) {
291
    getStore(context).put(TEST_ENVIRONMENT_KEY, testEnvironment);
1 ✔
292
  }
1 ✔
293

294
  private Worker getWorker(ExtensionContext context) {
295
    return getStore(context).get(WORKER_KEY, Worker.class);
1 ✔
296
  }
297

298
  private void setWorker(ExtensionContext context, Worker worker) {
299
    getStore(context).put(WORKER_KEY, worker);
1 ✔
300
  }
1 ✔
301

302
  private Endpoint getNexusEndpoint(ExtensionContext context) {
303
    return getStore(context).get(NEXUS_ENDPOINT_KEY, Endpoint.class);
1 ✔
304
  }
305

306
  private void setNexusEndpoint(ExtensionContext context, Endpoint endpoint) {
307
    getStore(context).put(NEXUS_ENDPOINT_KEY, endpoint);
1 ✔
308
  }
1 ✔
309

310
  private WorkflowOptions getWorkflowOptions(ExtensionContext context) {
311
    return getStore(context).get(WORKFLOW_OPTIONS_KEY, WorkflowOptions.class);
1 ✔
312
  }
313

314
  private void setWorkflowOptions(ExtensionContext context, WorkflowOptions taskQueue) {
315
    getStore(context).put(WORKFLOW_OPTIONS_KEY, taskQueue);
1 ✔
316
  }
1 ✔
317

318
  private ExtensionContext.Store getStore(ExtensionContext context) {
319
    Namespace namespace =
1 ✔
320
        Namespace.create(TestWorkflowExtension.class, context.getRequiredTestMethod());
1 ✔
321
    return context.getStore(namespace);
1 ✔
322
  }
323

324
  public static class Builder {
325

326
    private static final Object[] NO_ACTIVITIES = new Object[0];
1 ✔
327
    private static final Object[] NO_NEXUS_SERVICES = new Object[0];
1 ✔
328

329
    private WorkerOptions workerOptions = WorkerOptions.getDefaultInstance();
1 ✔
330
    private WorkflowClientOptions workflowClientOptions;
331
    private ActivityClientOptions activityClientOptions;
332
    private WorkerFactoryOptions workerFactoryOptions;
333
    private String namespace = "UnitTest";
1 ✔
334
    private Map<Class<?>, WorkflowImplementationOptions> workflowTypes = new HashMap<>();
1 ✔
335
    private Object[] activityImplementations = NO_ACTIVITIES;
1 ✔
336
    private Object[] nexusServiceImplementations = NO_NEXUS_SERVICES;
1 ✔
337
    private ServiceType serviceType = ServiceType.IN_MEMORY;
1 ✔
338
    private TemporalDevServerOptions devServerOptions =
1 ✔
339
        TemporalDevServerOptions.getDefaultInstance();
1 ✔
340
    private String target = null;
1 ✔
341
    private boolean doNotStart = false;
1 ✔
342
    private boolean doNotSetupNexusEndpoint = false;
1 ✔
343
    private long initialTimeMillis;
344
    // Default to TestEnvironmentOptions isUseTimeskipping
345
    private boolean useTimeskipping =
1 ✔
346
        TestEnvironmentOptions.getDefaultInstance().isUseTimeskipping();
1 ✔
347
    @Nonnull private Map<String, IndexedValueType> searchAttributes = new HashMap<>();
1 ✔
348
    private Scope metricsScope;
349

350
    private Builder() {}
1 ✔
351

352
    /**
353
     * @see TestWorkflowEnvironment#newWorker(String, WorkerOptions)
354
     */
355
    public Builder setWorkerOptions(WorkerOptions options) {
356
      this.workerOptions = options;
×
357
      return this;
×
358
    }
359

360
    /**
361
     * Override {@link WorkflowClientOptions} for test environment. If set, takes precedence over
362
     * {@link #setNamespace(String) namespace}.
363
     */
364
    public Builder setWorkflowClientOptions(WorkflowClientOptions workflowClientOptions) {
365
      this.workflowClientOptions = workflowClientOptions;
×
366
      return this;
×
367
    }
368

369
    /** Override {@link ActivityClientOptions} for test environment. */
370
    public Builder setActivityClientOptions(ActivityClientOptions activityClientOptions) {
371
      this.activityClientOptions = activityClientOptions;
×
372
      return this;
×
373
    }
374

375
    /**
376
     * Override {@link WorkerFactoryOptions} for test environment.
377
     *
378
     * @see TestEnvironmentOptions.Builder#setWorkerFactoryOptions(WorkerFactoryOptions)
379
     * @see io.temporal.worker.WorkerFactory#newInstance(WorkflowClient, WorkerFactoryOptions)
380
     */
381
    public Builder setWorkerFactoryOptions(WorkerFactoryOptions workerFactoryOptions) {
382
      this.workerFactoryOptions = workerFactoryOptions;
×
383
      return this;
×
384
    }
385

386
    /**
387
     * Set Temporal namespace to use for tests, by default, {@code UnitTest} is used.
388
     *
389
     * @see WorkflowClientOptions#getNamespace()
390
     */
391
    public Builder setNamespace(String namespace) {
392
      this.namespace = namespace;
×
393
      return this;
×
394
    }
395

396
    /**
397
     * Specify workflow implementation types to register with the Temporal worker.
398
     *
399
     * @see Worker#registerWorkflowImplementationTypes(Class[])
400
     */
401
    public Builder registerWorkflowImplementationTypes(Class<?>... workflowTypes) {
402
      WorkflowImplementationOptions defaultOptions =
403
          WorkflowImplementationOptions.newBuilder().build();
1 ✔
404
      Arrays.stream(workflowTypes).forEach(wf -> this.workflowTypes.put(wf, defaultOptions));
1 ✔
405
      return this;
1 ✔
406
    }
407

408
    /**
409
     * Specify workflow implementation types to register with the Temporal worker.
410
     *
411
     * @see Worker#registerWorkflowImplementationTypes(Class[])
412
     */
413
    public Builder registerWorkflowImplementationTypes(
414
        WorkflowImplementationOptions options, Class<?>... workflowTypes) {
415
      Arrays.stream(workflowTypes).forEach(wf -> this.workflowTypes.put(wf, options));
1 ✔
416
      return this;
1 ✔
417
    }
418

419
    /**
420
     * Specify Nexus service implementations to register with the Temporal workerIf any Nexus
421
     * services are registered with the worker, the extension will automatically create a Nexus
422
     * Endpoint for the test and the endpoint will be set on the per-service options and default
423
     * options in {@link WorkflowImplementationOptions} if none are provided.
424
     *
425
     * <p>This can be disabled by setting {@link #setDoNotSetupNexusEndpoint(boolean)} to true.
426
     *
427
     * @see Worker#registerNexusServiceImplementation(Object...)
428
     */
429
    public Builder setNexusServiceImplementation(Object... nexusServiceImplementations) {
430
      this.nexusServiceImplementations = nexusServiceImplementations;
1 ✔
431
      return this;
1 ✔
432
    }
433

434
    /**
435
     * Specify workflow implementation types to register with the Temporal worker.
436
     *
437
     * @see Worker#registerWorkflowImplementationTypes(Class[])
438
     * @deprecated use registerWorkflowImplementationTypes instead
439
     */
440
    @Deprecated
441
    public Builder setWorkflowTypes(Class<?>... workflowTypes) {
442
      return this.registerWorkflowImplementationTypes(workflowTypes);
×
443
    }
444

445
    /**
446
     * Specify activity implementations to register with the Temporal worker
447
     *
448
     * @see Worker#registerActivitiesImplementations(Object...)
449
     */
450
    public Builder setActivityImplementations(Object... activityImplementations) {
451
      this.activityImplementations = activityImplementations;
1 ✔
452
      return this;
1 ✔
453
    }
454

455
    /**
456
     * Switches to external Temporal service implementation with default endpoint of {@code
457
     * 127.0.0.1:7233}.
458
     *
459
     * @see TestEnvironmentOptions.Builder#setUseExternalService(boolean)
460
     * @see TestEnvironmentOptions.Builder#setTarget(String)
461
     * @see WorkflowServiceStubsOptions.Builder#setTarget(String)
462
     */
463
    public Builder useExternalService() {
464
      return useExternalService(null);
×
465
    }
466

467
    /**
468
     * Switches to external Temporal service implementation.
469
     *
470
     * @param target defines the endpoint which will be used for the communication with standalone
471
     *     Temporal service.
472
     * @see TestEnvironmentOptions.Builder#setUseExternalService(boolean)
473
     * @see TestEnvironmentOptions.Builder#setTarget(String)
474
     * @see WorkflowServiceStubsOptions.Builder#setTarget(String)
475
     */
476
    public Builder useExternalService(String target) {
477
      this.serviceType = ServiceType.EXTERNAL;
×
478
      this.target = target;
×
479
      return this;
×
480
    }
481

482
    /**
483
     * Uses an owned local Temporal dev server instead of the in-memory or external service.
484
     *
485
     * <p>The extension closes the server after each test. Dev-server tests do not support time
486
     * skipping.
487
     */
488
    public Builder useDevServer() {
489
      return useDevServer(TemporalDevServerOptions.getDefaultInstance());
×
490
    }
491

492
    /**
493
     * Uses an owned local Temporal dev server with the supplied options.
494
     *
495
     * <pre>{@code
496
     * TestWorkflowExtension.newBuilder()
497
     *     .useDevServer(TemporalDevServerOptions.newBuilder().setUiEnabled(true).build())
498
     *     .setWorkflowTypes(MyWorkflowImpl.class)
499
     *     .build();
500
     * }</pre>
501
     */
502
    public Builder useDevServer(@Nonnull TemporalDevServerOptions options) {
503
      if (options == null) {
1 !
504
        throw new NullPointerException("options");
×
505
      }
506
      this.serviceType = ServiceType.DEV_SERVER;
1 ✔
507
      this.target = null;
1 ✔
508
      this.devServerOptions = options;
1 ✔
509
      return this;
1 ✔
510
    }
511

512
    /** Switches to internal in-memory Temporal service implementation (default). */
513
    public Builder useInternalService() {
514
      this.serviceType = ServiceType.IN_MEMORY;
×
515
      this.target = null;
×
516
      return this;
×
517
    }
518

519
    /**
520
     * When set to true the {@link TestWorkflowEnvironment#start()} is not called by the extension
521
     * before executing the test. This to support tests that register activities and workflows with
522
     * workers directly instead of using only {@link TestWorkflowExtension.Builder}.
523
     */
524
    public Builder setDoNotStart(boolean doNotStart) {
525
      this.doNotStart = doNotStart;
×
526
      return this;
×
527
    }
528

529
    /**
530
     * When set to true the {@link TestWorkflowEnvironment} will not automatically create a Nexus
531
     * Endpoint. This is useful when you want to manually create a Nexus Endpoint for your test.
532
     */
533
    public Builder setDoNotSetupNexusEndpoint(boolean doNotSetupNexusEndpoint) {
534
      this.doNotSetupNexusEndpoint = doNotSetupNexusEndpoint;
×
535
      return this;
×
536
    }
537

538
    /**
539
     * Set the initial time for the workflow virtual clock, milliseconds since epoch.
540
     *
541
     * <p>Default is current time
542
     */
543
    public Builder setInitialTimeMillis(long initialTimeMillis) {
544
      this.initialTimeMillis = initialTimeMillis;
×
545
      return this;
×
546
    }
547

548
    /**
549
     * Set the initial time for the workflow virtual clock.
550
     *
551
     * <p>Default is current time
552
     */
553
    public Builder setInitialTime(Instant initialTime) {
554
      this.initialTimeMillis = initialTime.toEpochMilli();
1 ✔
555
      return this;
1 ✔
556
    }
557

558
    /**
559
     * Sets TestEnvironmentOptions.setUseTimeskippings. If true, no actual wall-clock time will pass
560
     * when a workflow sleeps or sets a timer.
561
     *
562
     * <p>Default is true
563
     */
564
    public Builder setUseTimeskipping(boolean useTimeskipping) {
565
      this.useTimeskipping = useTimeskipping;
×
566
      return this;
×
567
    }
568

569
    /**
570
     * Add a search attribute to be registered on the Temporal Server.
571
     *
572
     * @param name name of the search attribute
573
     * @param type search attribute type
574
     * @return {@code this}
575
     * @see <a
576
     *     href="https://docs.temporal.io/self-hosted-guide/visibility#create-custom-search-attributes">
577
     *     How to create custom Search Attributes</a>
578
     */
579
    public Builder registerSearchAttribute(String name, IndexedValueType type) {
580
      this.searchAttributes.put(name, type);
×
581
      return this;
×
582
    }
583

584
    /**
585
     * Sets the scope to be used for metrics reporting. Optional. Default is to not report metrics.
586
     *
587
     * <p>Note: Don't mock {@link Scope} in tests! If you need to verify the metrics behavior,
588
     * create a real Scope and mock, stub or spy a reporter instance:<br>
589
     *
590
     * <pre>{@code
591
     * StatsReporter reporter = mock(StatsReporter.class);
592
     * Scope metricsScope =
593
     *     new RootScopeBuilder()
594
     *         .reporter(reporter)
595
     *         .reportEvery(com.uber.m3.util.Duration.ofMillis(10));
596
     * }</pre>
597
     *
598
     * @param metricsScope the scope to be used for metrics reporting.
599
     * @return {@code this}
600
     */
601
    public Builder setMetricsScope(Scope metricsScope) {
602
      this.metricsScope = metricsScope;
×
603
      return this;
×
604
    }
605

606
    public TestWorkflowExtension build() {
607
      return new TestWorkflowExtension(this);
1 ✔
608
    }
609
  }
610
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc