Interface TestWorkflowEnvironment
- All Superinterfaces:
AutoCloseable,Closeable
- All Known Implementing Classes:
TestWorkflowEnvironmentInternal
Testing the workflow code is hard as it might be potentially very long-running. The included in-memory implementation of the Temporal service supports an automatic time skipping. Anytime a workflow under the test as well as the unit test code are waiting on a timer (or sleep) the internal service time is automatically advanced to the nearest time that unblocks one of the waiting threads. This way a workflow that runs in production for months is unit tested in milliseconds. Here is an example of a test that executes in a few milliseconds instead of over two hours that are needed for the workflow to complete:
public class SignaledWorkflowImpl implements SignaledWorkflow {
private String signalInput;
@Override
public String workflow1(String input) {
Workflow.sleep(Duration.ofHours(1));
Workflow.await(() -> signalInput != null);
Workflow.sleep(Duration.ofHours(1));
return signalInput + "-" + input;
}
@Override
public void processSignal(String input) {
signalInput = input;
}
}
@Test
public void testSignal() throws ExecutionException, InterruptedException {
TestWorkflowEnvironment testEnvironment = TestWorkflowEnvironment.newInstance();
// Creates a worker that polls tasks from the service owned by the testEnvironment.
Worker worker = testEnvironment.newWorker(TASK_QUEUE);
worker.registerWorkflowImplementationTypes(SignaledWorkflowImpl.class);
worker.start();
// Creates a WorkflowClient that interacts with the server owned by the testEnvironment.
WorkflowClient client = testEnvironment.getWorkflowClient();
SignaledWorkflow workflow = client.newWorkflowStub(SignaledWorkflow.class);
// Starts a workflow execution
CompletableFuture result = WorkflowClient.execute(workflow::workflow1, "input1");
// The sleep forwards the service clock for 65 minutes without blocking.
// This ensures that the signal is sent after the one hour sleep in the workflow code.
testEnvironment.sleep(Duration.ofMinutes(65));
workflow.processSignal("signalInput");
// Blocks until workflow is complete. Workflow sleep forwards clock for one hour and
// this call returns almost immediately.
assertEquals("signalInput-input1", result.get());
// Closes workers and releases in-memory service.
testEnvironment.close();
}
-
Method Summary
Modifier and TypeMethodDescriptionvoidawaitTermination(long timeout, TimeUnit unit) Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.voidclose()CallsshutdownNow()andawaitTermination(long, TimeUnit).io.temporal.api.nexus.v1.EndpointcreateNexusEndpoint(String name, String taskQueue) Register a Nexus Endpoint with the server.longThis time might not be equal toSystem.currentTimeMillis()due to time skipping.voiddeleteNexusEndpoint(io.temporal.api.nexus.v1.Endpoint endpoint) Delete a Nexus Endpoint on the server.io.temporal.client.ActivityClientCreates an ActivityClient that is connected to the in-memory test Temporal service.Currently prints histories of all workflow instances stored in the service.io.temporal.serviceclient.OperatorServiceStubsio.temporal.worker.WorkerFactoryio.temporal.client.WorkflowClientCreates a WorkflowClient that is connected to the in-memory test Temporal service.io.temporal.common.WorkflowExecutionHistorygetWorkflowExecutionHistory(io.temporal.api.common.v1.WorkflowExecution execution) Deprecated.io.temporal.serviceclient.WorkflowServiceStubsDeprecated.io.temporal.serviceclient.WorkflowServiceStubsbooleanWasshutdownNow()orshutdown()called?booleanWasstart()called?booleanAre all tasks done aftershutdownNow()orshutdown()?static TestWorkflowEnvironmentCreates the environment with default options.static TestWorkflowEnvironmentnewInstance(TestEnvironmentOptions options) Creates the environment with suppliedoptions.io.temporal.worker.WorkerCreates a new Worker instance that is connected to the in-memory test Temporal service.io.temporal.worker.WorkerCreates a new Worker instance that is connected to the in-memory test Temporal service.voidregisterDelayedCallback(Duration delay, Runnable r) Registers a callback to run after the specified delay according to the test Temporal service internal clock.booleanregisterSearchAttribute(String name, io.temporal.api.enums.v1.IndexedValueType type) Register a Search Attribute with the server.voidshutdown()Initiates an orderly shutdown in which polls are stopped and already received workflow and activity tasks are executed.voidInitiates an orderly shutdown in which polls are stopped and already received workflow and activity tasks are attempted to be stopped.voidDeprecated.voidWait until internal test Temporal service time passes the specified duration.voidstart()Start all workers created by this factory.static TestWorkflowEnvironmentStarts a local Temporal dev server and returns an environment that owns it.static TestWorkflowEnvironmentstartLocal(TemporalDevServerOptions serverOptions) Starts a local Temporal dev server with the supplied server options.static TestWorkflowEnvironmentstartLocal(TestEnvironmentOptions testOptions) Starts a local Temporal dev server using the environment namespace and returns an environment that owns it.static TestWorkflowEnvironmentstartLocal(TestEnvironmentOptions testOptions, TemporalDevServerOptions serverOptions) Starts a local Temporal dev server and returns an environment that owns it.
-
Method Details
-
newInstance
Creates the environment with default options. -
newInstance
Creates the environment with suppliedoptions. -
startLocal
Starts a local Temporal dev server and returns an environment that owns it.Unlike the in-memory test server, a local dev-server environment does not support time skipping.
try (TestWorkflowEnvironment environment = TestWorkflowEnvironment.startLocal()) { Worker worker = environment.newWorker("test-task-queue"); // Register implementations and run workflows against the local dev server. } -
startLocal
@Experimental static TestWorkflowEnvironment startLocal(@Nullable TestEnvironmentOptions testOptions) Starts a local Temporal dev server using the environment namespace and returns an environment that owns it. Local dev-server environments do not support time skipping. -
startLocal
@Experimental static TestWorkflowEnvironment startLocal(@Nonnull TemporalDevServerOptions serverOptions) Starts a local Temporal dev server with the supplied server options. Local dev-server environments do not support time skipping. -
startLocal
@Experimental static TestWorkflowEnvironment startLocal(@Nullable TestEnvironmentOptions testOptions, @Nonnull TemporalDevServerOptions serverOptions) Starts a local Temporal dev server and returns an environment that owns it.The namespace in
testOptionsis authoritative and is created by the dev server. Local dev-server environments do not support time skipping. -
newWorker
Creates a new Worker instance that is connected to the in-memory test Temporal service.- Parameters:
taskQueue- task queue to poll.
-
newWorker
Creates a new Worker instance that is connected to the in-memory test Temporal service.- Parameters:
taskQueue- task queue to poll.
-
getWorkflowClient
io.temporal.client.WorkflowClient getWorkflowClient()Creates a WorkflowClient that is connected to the in-memory test Temporal service. -
getActivityClient
io.temporal.client.ActivityClient getActivityClient()Creates an ActivityClient that is connected to the in-memory test Temporal service. -
currentTimeMillis
long currentTimeMillis()This time might not be equal toSystem.currentTimeMillis()due to time skipping.- Returns:
- the current in-memory test Temporal service time in milliseconds or
System.currentTimeMillis()if an external service without time skipping support is used
-
sleep
Wait until internal test Temporal service time passes the specified duration. This call also indicates that workflow time might jump forward (if none of the activities are running) up to the specified duration.This method falls back to
Thread.sleep(long)if an external service without time skipping support is used -
registerDelayedCallback
-
registerSearchAttribute
Register a Search Attribute with the server.- Parameters:
name- Search Attribute nametype- Search Attribute type to be used for an elastic search index- Returns:
trueif the search attribute was registered, false if it was registered already- See Also:
-
createNexusEndpoint
-
deleteNexusEndpoint
void deleteNexusEndpoint(io.temporal.api.nexus.v1.Endpoint endpoint) Delete a Nexus Endpoint on the server.- Parameters:
endpoint- current endpoint to be deleted
-
getWorkflowService
Deprecated.- Returns:
- the in-memory test Temporal service that is owned by this.
-
getWorkflowServiceStubs
io.temporal.serviceclient.WorkflowServiceStubs getWorkflowServiceStubs()- Returns:
WorkflowServiceStubsconnected to the test server (in-memory or external)
-
getOperatorServiceStubs
io.temporal.serviceclient.OperatorServiceStubs getOperatorServiceStubs()- Returns:
OperatorServiceStubsconnected to the test server
-
getNamespace
String getNamespace() -
getDiagnostics
String getDiagnostics()Currently prints histories of all workflow instances stored in the service. This is useful information to print in the case of a unit test failure. A convenient way to achieve this is to add the following Rule to a unit test:@Rule public TestWatcher watchman = new TestWatcher() { @Override protected void failed(Throwable e, Description description) { System.err.println(testEnvironment.getDiagnostics()); testEnvironment.close(); } };- Returns:
- the diagnostic data about the internal service state.
-
getWorkflowExecutionHistory
@Deprecated io.temporal.common.WorkflowExecutionHistory getWorkflowExecutionHistory(@Nonnull io.temporal.api.common.v1.WorkflowExecution execution) Deprecated.useWorkflowClient.fetchHistory(String, String)- Parameters:
execution- identifies the workflowId and runId (optionally) to reach the history for- Returns:
- history of the execution
-
close
void close()CallsshutdownNow()andawaitTermination(long, TimeUnit).- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable
-
getWorkerFactory
io.temporal.worker.WorkerFactory getWorkerFactory() -
start
void start()Start all workers created by this factory. -
isStarted
boolean isStarted()Wasstart()called? -
isShutdown
boolean isShutdown()WasshutdownNow()orshutdown()called? -
isTerminated
boolean isTerminated()Are all tasks done aftershutdownNow()orshutdown()? -
shutdownTestService
Deprecated.Initiates Test Service shutdown. This method is temporarily exposed to solve long poll thread shutdown forio.temporal.workflow.interceptorsTests.InterceptorExceptionTests#testExceptionOnStart(). See issue: https://github.com/temporalio/sdk-java/issues/608 -
shutdown
void shutdown()Initiates an orderly shutdown in which polls are stopped and already received workflow and activity tasks are executed. After the shutdown calls toActivityExecutionContext.heartbeat(Object)start throwingActivityWorkerShutdownException. Invocation has no additional effect if already shut down. This method does not wait for previously received tasks to complete execution. UseawaitTermination(long, TimeUnit)to do that. -
shutdownNow
void shutdownNow()Initiates an orderly shutdown in which polls are stopped and already received workflow and activity tasks are attempted to be stopped. This implementation cancels tasks via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate. Also, after the shutdownNow calls toActivityExecutionContext.heartbeat(Object)start throwingActivityWorkerShutdownException. Invocation has no additional effect if already shut down. This method does not wait for previously received tasks to complete execution. UseawaitTermination(long, TimeUnit)to do that. -
awaitTermination
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
-
WorkflowClient.fetchHistory(String, String)