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

temporalio / sdk-java / #351

04 Aug 2026 11:58PM UTC coverage: 67.781% (-0.5%) from 68.245%
#351

push

github

web-flow
Add dev server downloader & runner to testing package (#2982)

7311 of 12876 branches covered (56.78%)

Branch coverage included in aggregate %.

331 of 703 new or added lines in 12 files covered. (47.08%)

28 existing lines in 13 files now uncovered.

30207 of 42476 relevant lines covered (71.12%)

0.71 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.Experimental;
14
import io.temporal.common.metadata.POJOWorkflowImplMetadata;
15
import io.temporal.common.metadata.POJOWorkflowInterfaceMetadata;
16
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
17
import io.temporal.worker.Worker;
18
import io.temporal.worker.WorkerFactoryOptions;
19
import io.temporal.worker.WorkerOptions;
20
import io.temporal.worker.WorkflowImplementationOptions;
21
import io.temporal.workflow.DynamicWorkflow;
22
import java.lang.reflect.Constructor;
23
import java.lang.reflect.Parameter;
24
import java.time.Instant;
25
import java.util.*;
26
import javax.annotation.Nonnull;
27
import org.junit.jupiter.api.extension.AfterEachCallback;
28
import org.junit.jupiter.api.extension.BeforeEachCallback;
29
import org.junit.jupiter.api.extension.ExtensionContext;
30
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
31
import org.junit.jupiter.api.extension.ParameterContext;
32
import org.junit.jupiter.api.extension.ParameterResolutionException;
33
import org.junit.jupiter.api.extension.ParameterResolver;
34
import org.junit.jupiter.api.extension.TestWatcher;
35
import org.junit.platform.commons.support.AnnotationSupport;
36

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

325
  public static class Builder {
326

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

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

351
    private Builder() {}
1✔
352

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

609
    public TestWorkflowExtension build() {
610
      return new TestWorkflowExtension(this);
1✔
611
    }
612
  }
613
}
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