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

temporalio / sdk-java / #333

16 Oct 2024 07:28PM UTC coverage: 78.65% (+0.6%) from 78.085%
#333

push

github

web-flow
Fix code coverage (#2275)

22670 of 28824 relevant lines covered (78.65%)

0.79 hits per line

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

79.19
/temporal-testing/src/main/java/io/temporal/testing/TestWorkflowExtension.java
1
/*
2
 * Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
3
 *
4
 * Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
 *
6
 * Modifications copyright (C) 2017 Uber Technologies, Inc.
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this material except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *   http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20

21
package io.temporal.testing;
22

23
import static io.temporal.testing.internal.TestServiceUtils.applyNexusServiceOptions;
24

25
import com.uber.m3.tally.Scope;
26
import io.temporal.api.enums.v1.IndexedValueType;
27
import io.temporal.api.nexus.v1.Endpoint;
28
import io.temporal.client.WorkflowClient;
29
import io.temporal.client.WorkflowClientOptions;
30
import io.temporal.client.WorkflowOptions;
31
import io.temporal.common.Experimental;
32
import io.temporal.common.metadata.POJOWorkflowImplMetadata;
33
import io.temporal.common.metadata.POJOWorkflowInterfaceMetadata;
34
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
35
import io.temporal.worker.Worker;
36
import io.temporal.worker.WorkerFactoryOptions;
37
import io.temporal.worker.WorkerOptions;
38
import io.temporal.worker.WorkflowImplementationOptions;
39
import io.temporal.workflow.DynamicWorkflow;
40
import java.lang.reflect.Constructor;
41
import java.lang.reflect.Parameter;
42
import java.time.Instant;
43
import java.util.*;
44
import javax.annotation.Nonnull;
45
import org.junit.jupiter.api.extension.AfterEachCallback;
46
import org.junit.jupiter.api.extension.BeforeEachCallback;
47
import org.junit.jupiter.api.extension.ExtensionContext;
48
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
49
import org.junit.jupiter.api.extension.ParameterContext;
50
import org.junit.jupiter.api.extension.ParameterResolutionException;
51
import org.junit.jupiter.api.extension.ParameterResolver;
52
import org.junit.jupiter.api.extension.TestWatcher;
53
import org.junit.platform.commons.support.AnnotationSupport;
54

55
/**
56
 * JUnit Jupiter extension that simplifies testing of Temporal workflows.
57
 *
58
 * <p>The extension manages Temporal test environment and workflow worker lifecycle, and be used
59
 * with both in-memory (default) and standalone temporal service (see {@link
60
 * Builder#useInternalService()}, {@link Builder#useExternalService()} and {@link
61
 * Builder#useExternalService(String)}}).
62
 *
63
 * <p>This extension can inject workflow stubs as well as instances of {@link
64
 * TestWorkflowEnvironment}, {@link WorkflowClient}, {@link WorkflowOptions}, {@link Worker}, into
65
 * test methods.
66
 *
67
 * <p>Usage example:
68
 *
69
 * <pre><code>
70
 * public class MyTest {
71
 *
72
 *  {@literal @}RegisterExtension
73
 *   public static final TestWorkflowExtension workflowExtension =
74
 *       TestWorkflowExtension.newBuilder()
75
 *           .setWorkflowTypes(MyWorkflowImpl.class)
76
 *           .setActivityImplementations(new MyActivities())
77
 *           .build();
78
 *
79
 *  {@literal @}Test
80
 *   public void testMyWorkflow(MyWorkflow workflow) {
81
 *     // Test code that calls MyWorkflow methods
82
 *   }
83
 * }
84
 * </code></pre>
85
 */
86
public class TestWorkflowExtension
87
    implements ParameterResolver, TestWatcher, BeforeEachCallback, AfterEachCallback {
88

89
  private static final String TEST_ENVIRONMENT_KEY = "testEnvironment";
90
  private static final String WORKER_KEY = "worker";
91
  private static final String WORKFLOW_OPTIONS_KEY = "workflowOptions";
92
  private static final String NEXUS_ENDPOINT_KEY = "nexusEndpoint";
93

94
  private final WorkerOptions workerOptions;
95
  private final WorkflowClientOptions workflowClientOptions;
96
  private final WorkerFactoryOptions workerFactoryOptions;
97
  private final Map<Class<?>, WorkflowImplementationOptions> workflowTypes;
98
  private final Object[] activityImplementations;
99
  private final Object[] nexusServiceImplementations;
100
  private final boolean useExternalService;
101
  private final String target;
102
  private final boolean doNotStart;
103
  private final boolean doNotSetupNexusEndpoint;
104
  private final long initialTimeMillis;
105
  private final boolean useTimeskipping;
106
  @Nonnull private final Map<String, IndexedValueType> searchAttributes;
107
  private final Scope metricsScope;
108

109
  private final Set<Class<?>> supportedParameterTypes = new HashSet<>();
1✔
110
  private boolean includesDynamicWorkflow;
111

112
  private TestWorkflowExtension(Builder builder) {
1✔
113
    workerOptions = builder.workerOptions;
1✔
114
    if (builder.workflowClientOptions != null) {
1✔
115
      workflowClientOptions = builder.workflowClientOptions;
×
116
    } else {
117
      workflowClientOptions =
1✔
118
          WorkflowClientOptions.newBuilder().setNamespace(builder.namespace).build();
1✔
119
    }
120
    workerFactoryOptions = builder.workerFactoryOptions;
1✔
121
    workflowTypes = builder.workflowTypes;
1✔
122
    activityImplementations = builder.activityImplementations;
1✔
123
    nexusServiceImplementations = builder.nexusServiceImplementations;
1✔
124
    useExternalService = builder.useExternalService;
1✔
125
    target = builder.target;
1✔
126
    doNotStart = builder.doNotStart;
1✔
127
    doNotSetupNexusEndpoint = builder.doNotSetupNexusEndpoint;
1✔
128
    initialTimeMillis = builder.initialTimeMillis;
1✔
129
    useTimeskipping = builder.useTimeskipping;
1✔
130
    this.searchAttributes = builder.searchAttributes;
1✔
131
    this.metricsScope = builder.metricsScope;
1✔
132

133
    supportedParameterTypes.add(TestWorkflowEnvironment.class);
1✔
134
    supportedParameterTypes.add(WorkflowClient.class);
1✔
135
    supportedParameterTypes.add(WorkflowOptions.class);
1✔
136
    supportedParameterTypes.add(Worker.class);
1✔
137

138
    for (Class<?> workflowType : workflowTypes.keySet()) {
1✔
139
      if (DynamicWorkflow.class.isAssignableFrom(workflowType)) {
1✔
140
        includesDynamicWorkflow = true;
1✔
141
        continue;
1✔
142
      }
143
      POJOWorkflowImplMetadata metadata = POJOWorkflowImplMetadata.newInstance(workflowType);
1✔
144
      for (POJOWorkflowInterfaceMetadata workflowInterface : metadata.getWorkflowInterfaces()) {
1✔
145
        supportedParameterTypes.add(workflowInterface.getInterfaceClass());
1✔
146
      }
1✔
147
    }
1✔
148
  }
1✔
149

150
  public static Builder newBuilder() {
151
    return new Builder();
1✔
152
  }
153

154
  @Override
155
  public boolean supportsParameter(
156
      ParameterContext parameterContext, ExtensionContext extensionContext)
157
      throws ParameterResolutionException {
158

159
    Parameter parameter = parameterContext.getParameter();
1✔
160
    if (parameter.getDeclaringExecutable() instanceof Constructor) {
1✔
161
      // Constructor injection is not supported
162
      return false;
×
163
    }
164

165
    Class<?> parameterType = parameter.getType();
1✔
166
    if (supportedParameterTypes.contains(parameterType)) {
1✔
167
      return true;
1✔
168
    }
169

170
    if (!includesDynamicWorkflow) {
1✔
171
      // If no DynamicWorkflow implementation was registered then supportedParameterTypes are the
172
      // only ones types that can be injected
173
      return false;
×
174
    }
175

176
    try {
177
      // If POJOWorkflowInterfaceMetadata can be instantiated then parameterType is a proper
178
      // workflow interface and can be injected
179
      POJOWorkflowInterfaceMetadata.newInstance(parameterType);
1✔
180
      return true;
1✔
181
    } catch (Exception e) {
×
182
      return false;
×
183
    }
184
  }
185

186
  @Override
187
  public Object resolveParameter(
188
      ParameterContext parameterContext, ExtensionContext extensionContext)
189
      throws ParameterResolutionException {
190

191
    Class<?> parameterType = parameterContext.getParameter().getType();
1✔
192
    if (parameterType == TestWorkflowEnvironment.class) {
1✔
193
      return getTestEnvironment(extensionContext);
1✔
194
    } else if (parameterType == WorkflowClient.class) {
1✔
195
      return getTestEnvironment(extensionContext).getWorkflowClient();
1✔
196
    } else if (parameterType == WorkflowOptions.class) {
1✔
197
      return getWorkflowOptions(extensionContext);
1✔
198
    } else if (parameterType == Worker.class) {
1✔
199
      return getWorker(extensionContext);
1✔
200
    } else {
201
      // Workflow stub
202
      return getTestEnvironment(extensionContext)
1✔
203
          .getWorkflowClient()
1✔
204
          .newWorkflowStub(parameterType, getWorkflowOptions(extensionContext));
1✔
205
    }
206
  }
207

208
  @Override
209
  public void beforeEach(ExtensionContext context) {
210
    long currentInitialTimeMillis =
1✔
211
        AnnotationSupport.findAnnotation(context.getElement(), WorkflowInitialTime.class)
1✔
212
            .map(annotation -> Instant.parse(annotation.value()).toEpochMilli())
1✔
213
            .orElse(initialTimeMillis);
1✔
214

215
    TestWorkflowEnvironment testEnvironment =
1✔
216
        TestWorkflowEnvironment.newInstance(createTestEnvOptions(currentInitialTimeMillis));
1✔
217

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

234
    if (!doNotStart) {
1✔
235
      testEnvironment.start();
1✔
236
    }
237
    if (createNexusEndpoint) {
1✔
238
      setNexusEndpoint(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✔
244
  }
1✔
245

246
  protected TestEnvironmentOptions createTestEnvOptions(long initialTimeMillis) {
247
    return TestEnvironmentOptions.newBuilder()
1✔
248
        .setWorkflowClientOptions(workflowClientOptions)
1✔
249
        .setWorkerFactoryOptions(workerFactoryOptions)
1✔
250
        .setUseExternalService(useExternalService)
1✔
251
        .setUseTimeskipping(useTimeskipping)
1✔
252
        .setTarget(target)
1✔
253
        .setInitialTimeMillis(initialTimeMillis)
1✔
254
        .setMetricsScope(metricsScope)
1✔
255
        .setSearchAttributes(searchAttributes)
1✔
256
        .build();
1✔
257
  }
258

259
  @Override
260
  public void afterEach(ExtensionContext context) {
261
    Endpoint endpoint = getNexusEndpoint(context);
1✔
262
    TestWorkflowEnvironment testEnvironment = getTestEnvironment(context);
1✔
263
    if (endpoint != null && !testEnvironment.getOperatorServiceStubs().isShutdown()) {
1✔
264
      testEnvironment.deleteNexusEndpoint(endpoint);
1✔
265
    }
266
    testEnvironment.close();
1✔
267
  }
1✔
268

269
  @Override
270
  public void testFailed(ExtensionContext context, Throwable cause) {
271
    TestWorkflowEnvironment testEnvironment = getTestEnvironment(context);
×
272
    System.err.println("Workflow execution histories:\n" + testEnvironment.getDiagnostics());
×
273
  }
×
274

275
  private TestWorkflowEnvironment getTestEnvironment(ExtensionContext context) {
276
    return getStore(context).get(TEST_ENVIRONMENT_KEY, TestWorkflowEnvironment.class);
1✔
277
  }
278

279
  private void setTestEnvironment(
280
      ExtensionContext context, TestWorkflowEnvironment testEnvironment) {
281
    getStore(context).put(TEST_ENVIRONMENT_KEY, testEnvironment);
1✔
282
  }
1✔
283

284
  private Worker getWorker(ExtensionContext context) {
285
    return getStore(context).get(WORKER_KEY, Worker.class);
1✔
286
  }
287

288
  private void setWorker(ExtensionContext context, Worker worker) {
289
    getStore(context).put(WORKER_KEY, worker);
1✔
290
  }
1✔
291

292
  private Endpoint getNexusEndpoint(ExtensionContext context) {
293
    return getStore(context).get(NEXUS_ENDPOINT_KEY, Endpoint.class);
1✔
294
  }
295

296
  private void setNexusEndpoint(ExtensionContext context, Endpoint endpoint) {
297
    getStore(context).put(NEXUS_ENDPOINT_KEY, endpoint);
1✔
298
  }
1✔
299

300
  private WorkflowOptions getWorkflowOptions(ExtensionContext context) {
301
    return getStore(context).get(WORKFLOW_OPTIONS_KEY, WorkflowOptions.class);
1✔
302
  }
303

304
  private void setWorkflowOptions(ExtensionContext context, WorkflowOptions taskQueue) {
305
    getStore(context).put(WORKFLOW_OPTIONS_KEY, taskQueue);
1✔
306
  }
1✔
307

308
  private ExtensionContext.Store getStore(ExtensionContext context) {
309
    Namespace namespace =
1✔
310
        Namespace.create(TestWorkflowExtension.class, context.getRequiredTestMethod());
1✔
311
    return context.getStore(namespace);
1✔
312
  }
313

314
  public static class Builder {
315

316
    private static final Object[] NO_ACTIVITIES = new Object[0];
1✔
317
    private static final Object[] NO_NEXUS_SERVICES = new Object[0];
1✔
318

319
    private WorkerOptions workerOptions = WorkerOptions.getDefaultInstance();
1✔
320
    private WorkflowClientOptions workflowClientOptions;
321
    private WorkerFactoryOptions workerFactoryOptions;
322
    private String namespace = "UnitTest";
1✔
323
    private Map<Class<?>, WorkflowImplementationOptions> workflowTypes = new HashMap<>();
1✔
324
    private Object[] activityImplementations = NO_ACTIVITIES;
1✔
325
    private Object[] nexusServiceImplementations = NO_NEXUS_SERVICES;
1✔
326
    private boolean useExternalService = false;
1✔
327
    private String target = null;
1✔
328
    private boolean doNotStart = false;
1✔
329
    private boolean doNotSetupNexusEndpoint = false;
1✔
330
    private long initialTimeMillis;
331
    // Default to TestEnvironmentOptions isUseTimeskipping
332
    private boolean useTimeskipping =
1✔
333
        TestEnvironmentOptions.getDefaultInstance().isUseTimeskipping();
1✔
334
    @Nonnull private Map<String, IndexedValueType> searchAttributes = new HashMap<>();
1✔
335
    private Scope metricsScope;
336

337
    private Builder() {}
1✔
338

339
    /**
340
     * @see TestWorkflowEnvironment#newWorker(String, WorkerOptions)
341
     */
342
    public Builder setWorkerOptions(WorkerOptions options) {
343
      this.workerOptions = options;
×
344
      return this;
×
345
    }
346

347
    /**
348
     * Override {@link WorkflowClientOptions} for test environment. If set, takes precedence over
349
     * {@link #setNamespace(String) namespace}.
350
     */
351
    public Builder setWorkflowClientOptions(WorkflowClientOptions workflowClientOptions) {
352
      this.workflowClientOptions = workflowClientOptions;
×
353
      return this;
×
354
    }
355

356
    /**
357
     * Override {@link WorkerFactoryOptions} for test environment.
358
     *
359
     * @see TestEnvironmentOptions.Builder#setWorkerFactoryOptions(WorkerFactoryOptions)
360
     * @see io.temporal.worker.WorkerFactory#newInstance(WorkflowClient, WorkerFactoryOptions)
361
     */
362
    public Builder setWorkerFactoryOptions(WorkerFactoryOptions workerFactoryOptions) {
363
      this.workerFactoryOptions = workerFactoryOptions;
×
364
      return this;
×
365
    }
366

367
    /**
368
     * Set Temporal namespace to use for tests, by default, {@code UnitTest} is used.
369
     *
370
     * @see WorkflowClientOptions#getNamespace()
371
     */
372
    public Builder setNamespace(String namespace) {
373
      this.namespace = namespace;
×
374
      return this;
×
375
    }
376

377
    /**
378
     * Specify workflow implementation types to register with the Temporal worker.
379
     *
380
     * @see Worker#registerWorkflowImplementationTypes(Class[])
381
     */
382
    public Builder registerWorkflowImplementationTypes(Class<?>... workflowTypes) {
383
      WorkflowImplementationOptions defaultOptions =
384
          WorkflowImplementationOptions.newBuilder().build();
1✔
385
      Arrays.stream(workflowTypes).forEach(wf -> this.workflowTypes.put(wf, defaultOptions));
1✔
386
      return this;
1✔
387
    }
388

389
    /**
390
     * Specify workflow implementation types to register with the Temporal worker.
391
     *
392
     * @see Worker#registerWorkflowImplementationTypes(Class[])
393
     */
394
    public Builder registerWorkflowImplementationTypes(
395
        WorkflowImplementationOptions options, Class<?>... workflowTypes) {
396
      Arrays.stream(workflowTypes).forEach(wf -> this.workflowTypes.put(wf, options));
1✔
397
      return this;
1✔
398
    }
399

400
    /**
401
     * Specify Nexus service implementations to register with the Temporal workerIf any Nexus
402
     * services are registered with the worker, the extension will automatically create a Nexus
403
     * Endpoint for the test and the endpoint will be set on the per-service options and default
404
     * options in {@link WorkflowImplementationOptions} if none are provided.
405
     *
406
     * <p>This can be disabled by setting {@link #setDoNotSetupNexusEndpoint(boolean)} to true.
407
     *
408
     * @see Worker#registerNexusServiceImplementation(Object...)
409
     */
410
    @Experimental
411
    public Builder setNexusServiceImplementation(Object... nexusServiceImplementations) {
412
      this.nexusServiceImplementations = nexusServiceImplementations;
1✔
413
      return this;
1✔
414
    }
415

416
    /**
417
     * Specify workflow implementation types to register with the Temporal worker.
418
     *
419
     * @see Worker#registerWorkflowImplementationTypes(Class[])
420
     * @deprecated use registerWorkflowImplementationTypes instead
421
     */
422
    @Deprecated
423
    public Builder setWorkflowTypes(Class<?>... workflowTypes) {
424
      return this.registerWorkflowImplementationTypes(workflowTypes);
×
425
    }
426

427
    /**
428
     * Specify activity implementations to register with the Temporal worker
429
     *
430
     * @see Worker#registerActivitiesImplementations(Object...)
431
     */
432
    public Builder setActivityImplementations(Object... activityImplementations) {
433
      this.activityImplementations = activityImplementations;
1✔
434
      return this;
1✔
435
    }
436

437
    /**
438
     * Switches to external Temporal service implementation with default endpoint of {@code
439
     * 127.0.0.1:7233}.
440
     *
441
     * @see TestEnvironmentOptions.Builder#setUseExternalService(boolean)
442
     * @see TestEnvironmentOptions.Builder#setTarget(String)
443
     * @see WorkflowServiceStubsOptions.Builder#setTarget(String)
444
     */
445
    public Builder useExternalService() {
446
      return useExternalService(null);
×
447
    }
448

449
    /**
450
     * Switches to external Temporal service implementation.
451
     *
452
     * @param target defines the endpoint which will be used for the communication with standalone
453
     *     Temporal service.
454
     * @see TestEnvironmentOptions.Builder#setUseExternalService(boolean)
455
     * @see TestEnvironmentOptions.Builder#setTarget(String)
456
     * @see WorkflowServiceStubsOptions.Builder#setTarget(String)
457
     */
458
    public Builder useExternalService(String target) {
459
      this.useExternalService = true;
×
460
      this.target = target;
×
461
      return this;
×
462
    }
463

464
    /** Switches to internal in-memory Temporal service implementation (default). */
465
    public Builder useInternalService() {
466
      this.useExternalService = false;
×
467
      this.target = null;
×
468
      return this;
×
469
    }
470

471
    /**
472
     * When set to true the {@link TestWorkflowEnvironment#start()} is not called by the extension
473
     * before executing the test. This to support tests that register activities and workflows with
474
     * workers directly instead of using only {@link TestWorkflowExtension.Builder}.
475
     */
476
    public Builder setDoNotStart(boolean doNotStart) {
477
      this.doNotStart = doNotStart;
×
478
      return this;
×
479
    }
480

481
    /**
482
     * When set to true the {@link TestWorkflowEnvironment} will not automatically create a Nexus
483
     * Endpoint. This is useful when you want to manually create a Nexus Endpoint for your test.
484
     */
485
    @Experimental
486
    public Builder setDoNotSetupNexusEndpoint(boolean doNotSetupNexusEndpoint) {
487
      this.doNotSetupNexusEndpoint = doNotSetupNexusEndpoint;
×
488
      return this;
×
489
    }
490

491
    /**
492
     * Set the initial time for the workflow virtual clock, milliseconds since epoch.
493
     *
494
     * <p>Default is current time
495
     */
496
    public Builder setInitialTimeMillis(long initialTimeMillis) {
497
      this.initialTimeMillis = initialTimeMillis;
×
498
      return this;
×
499
    }
500

501
    /**
502
     * Set the initial time for the workflow virtual clock.
503
     *
504
     * <p>Default is current time
505
     */
506
    public Builder setInitialTime(Instant initialTime) {
507
      this.initialTimeMillis = initialTime.toEpochMilli();
1✔
508
      return this;
1✔
509
    }
510

511
    /**
512
     * Sets TestEnvironmentOptions.setUseTimeskippings. If true, no actual wall-clock time will pass
513
     * when a workflow sleeps or sets a timer.
514
     *
515
     * <p>Default is true
516
     */
517
    public Builder setUseTimeskipping(boolean useTimeskipping) {
518
      this.useTimeskipping = useTimeskipping;
×
519
      return this;
×
520
    }
521

522
    /**
523
     * Add a search attribute to be registered on the Temporal Server.
524
     *
525
     * @param name name of the search attribute
526
     * @param type search attribute type
527
     * @return {@code this}
528
     * @see <a
529
     *     href="https://docs.temporal.io/docs/tctl/how-to-add-a-custom-search-attribute-to-a-cluster-using-tctl">Add
530
     *     a Custom Search Attribute Using tctl</a>
531
     */
532
    public Builder registerSearchAttribute(String name, IndexedValueType type) {
533
      this.searchAttributes.put(name, type);
×
534
      return this;
×
535
    }
536

537
    /**
538
     * Sets the scope to be used for metrics reporting. Optional. Default is to not report metrics.
539
     *
540
     * <p>Note: Don't mock {@link Scope} in tests! If you need to verify the metrics behavior,
541
     * create a real Scope and mock, stub or spy a reporter instance:<br>
542
     *
543
     * <pre>{@code
544
     * StatsReporter reporter = mock(StatsReporter.class);
545
     * Scope metricsScope =
546
     *     new RootScopeBuilder()
547
     *         .reporter(reporter)
548
     *         .reportEvery(com.uber.m3.util.Duration.ofMillis(10));
549
     * }</pre>
550
     *
551
     * @param metricsScope the scope to be used for metrics reporting.
552
     * @return {@code this}
553
     */
554
    public Builder setMetricsScope(Scope metricsScope) {
555
      this.metricsScope = metricsScope;
×
556
      return this;
×
557
    }
558

559
    public TestWorkflowExtension build() {
560
      return new TestWorkflowExtension(this);
1✔
561
    }
562
  }
563
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc