diff --git a/docs/develop/dotnet/best-practices/testing-suite.mdx b/docs/develop/dotnet/best-practices/testing-suite.mdx index 8d7b967364..2032e38e85 100644 --- a/docs/develop/dotnet/best-practices/testing-suite.mdx +++ b/docs/develop/dotnet/best-practices/testing-suite.mdx @@ -103,6 +103,7 @@ While this is just a demonstration, a local server is often used as a fixture ac Sometimes there is a need to test Workflows that run a long time or to test that timeouts occur. A time-skipping `Temporalio.Testing.WorkflowEnvironment` can be started via `StartTimeSkippingAsync` which is a reimplementation of the Temporal server with special time skipping capabilities. + Like `StartLocalAsync`, this also lazily downloads the process to run when first called. Note, unlike `StartLocalAsync`, this class is not thread safe nor safe for use with independent tests. It can be technically be reused, but only for one test at a time because time skipping is locked/unlocked at the environment level. @@ -158,7 +159,7 @@ public async Task WaitADayWorkflow_SimpleRun_Succeeds() ``` This test will run almost instantly. -This is because by calling `ExecuteWorkflowAsync` on our client, we are actually calling `StartWorkflowAsync` + `GetResultAsync`, and `GetResultAsync` automatically skips time as much as it can (basically until the end of the workflow or until an activity is run). +This is because by calling `ExecuteWorkflowAsync` on our client, we are actually calling `StartWorkflowAsync` + `GetResultAsync`, and `GetResultAsync` automatically skips time as much as it can (basically until the end of the Workflow or until an Activity is run). To disable automatic time-skipping while waiting for a workflow result, run code as a lambda passed to `env.WithAutoTimeSkippingDisabled` or `env.WithAutoTimeSkippingDisabledAsync`. diff --git a/docs/develop/go/best-practices/testing-suite.mdx b/docs/develop/go/best-practices/testing-suite.mdx index 2e1421b6fa..f5464bdd87 100644 --- a/docs/develop/go/best-practices/testing-suite.mdx +++ b/docs/develop/go/best-practices/testing-suite.mdx @@ -54,10 +54,10 @@ For testing Workflows, we use a `testsuite.TestWorkflowEnvironment`. ```go type UnitTestSuite struct { - suite.Suite - testsuite.WorkflowTestSuite + suite.Suite + testsuite.WorkflowTestSuite - env *testsuite.TestWorkflowEnvironment + env *testsuite.TestWorkflowEnvironment } ``` @@ -66,7 +66,7 @@ Doing so ensures that each test runs in its own isolated sandbox. ```go func (s *UnitTestSuite) SetupTest() { - s.env = s.NewTestWorkflowEnvironment() + s.env = s.NewTestWorkflowEnvironment() } ``` @@ -75,7 +75,7 @@ Timeout for the entire test can be set using `SetTestTimeout` in the Workflow or ```go func (s *UnitTestSuite) AfterTest(suiteName, testName string) { - s.env.AssertExpectations(s.T()) + s.env.AssertExpectations(s.T()) } ``` @@ -83,7 +83,7 @@ Finally, we create a regular test function recognized by the `go test` command, ```go func TestUnitTestSuite(t *testing.T) { - suite.Run(t, new(UnitTestSuite)) + suite.Run(t, new(UnitTestSuite)) } ``` @@ -92,6 +92,18 @@ func TestUnitTestSuite(t *testing.T) { An Activity can be tested with a mock Activity environment, which provides a way to mock the Activity context, listen to Heartbeats, and cancel the Activity. This behavior allows you to test the Activity in isolation by calling it directly, without needing to create a Worker to run the Activity. +### Run an Activity {/* #run-an-activity */} + +If an Activity references its context, you need to mock that context when testing in isolation. + +### Listen to Heartbeats {/* #listen-to-heartbeats */} + +When an Activity sends a Heartbeat, be sure that you can see the Heartbeats in your test code so that you can verify them. + +### Cancel an Activity {/* #cancel-an-activity */} + +If an Activity is supposed to react to a Cancellation, you can test whether it reacts correctly by canceling it. + ## Mock and override Activities When running unit tests on Workflows, we want to test the Workflow logic in isolation. Additionally, we want to inject Activity errors during our test runs. The test framework provides two mechanisms that support these scenarios: Activity mocking and Activity overriding. Both of these mechanisms allow you to change the behavior of Activities invoked by your Workflow without the need to modify the actual Workflow code. @@ -100,17 +112,17 @@ Let's take a look at a test that simulates a test that fails via the "Activity m ```go func (s *UnitTestSuite) Test_SimpleWorkflow_ActivityFails() { - s.env.OnActivity(SimpleActivity, mock.Anything, mock.Anything).Return( - "", errors.New("SimpleActivityFailure")) - s.env.ExecuteWorkflow(SimpleWorkflow, "test_failure") + s.env.OnActivity(SimpleActivity, mock.Anything, mock.Anything).Return( + "", errors.New("SimpleActivityFailure")) + s.env.ExecuteWorkflow(SimpleWorkflow, "test_failure") - s.True(s.env.IsWorkflowCompleted()) + s.True(s.env.IsWorkflowCompleted()) - err := s.env.GetWorkflowError() - s.Error(err) - var applicationErr *temporal.ApplicationError - s.True(errors.As(err, &applicationErr)) - s.Equal("SimpleActivityFailure", applicationErr.Error()) + err := s.env.GetWorkflowError() + s.Error(err) + var applicationErr *temporal.ApplicationError + s.True(errors.As(err, &applicationErr)) + s.Equal("SimpleActivityFailure", applicationErr.Error()) } ``` @@ -129,15 +141,15 @@ Let's assume we want to validate that the Activity gets called with the correct ```go func (s *UnitTestSuite) Test_SimpleWorkflow_ActivityParamCorrect() { - s.env.OnActivity(SimpleActivity, mock.Anything, mock.Anything).Return( - func(ctx context.Context, value string) (string, error) { - s.Equal("test_success", value) - return value, nil - }) - s.env.ExecuteWorkflow(SimpleWorkflow, "test_success") - - s.True(s.env.IsWorkflowCompleted()) - s.NoError(s.env.GetWorkflowError()) + s.env.OnActivity(SimpleActivity, mock.Anything, mock.Anything).Return( + func(ctx context.Context, value string) (string, error) { + s.Equal("test_success", value) + return value, nil + }) + s.env.ExecuteWorkflow(SimpleWorkflow, "test_success") + + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) } ``` @@ -149,18 +161,6 @@ Additionally, the framework will validate that the signature of the "mock" funct Since this can be an entire function, there is no limitation as to what we can do here. In this example, we assert that the `value` param has the same content as the value param we passed to the Workflow. -### Run an Activity {/* #run-an-activity */} - -If an Activity references its context, you need to mock that context when testing in isolation. - -### Listen to Heartbeats {/* #listen-to-heartbeats */} - -When an Activity sends a Heartbeat, be sure that you can see the Heartbeats in your test code so that you can verify them. - -### Cancel an Activity {/* #cancel-an-activity */} - -If an Activity is supposed to react to a Cancellation, you can test whether it reacts correctly by canceling it. - ## Mock and override Nexus operations Mocking Nexus operations lets you test a Workflow that executes Nexus operations without needing a @@ -181,24 +181,24 @@ reference if you have it available. ```go func (s *UnitTestSuite) Test_SimpleWorkflow_NexusSyncOperation() { - s.env.OnNexusOperation( - "sample-service", - nexus.NewOperationReference[SampleInput, SampleOutput]("sample-operation"), - SampleInput{}, - workflow.NexusOperationOptions{}, - ).Return( - &nexus.HandlerStartOperationResultSync[SampleOutput]{ - Value: SampleOutput{}, - }, - nil, // error if you want to simulate an error in the ExecuteOperation call - ) - // You can also add a delay to return the mock values by calling After(). - // Eg: s.env.OnNexusOperation(...).Return(...).After(1*time.Second) - - s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") - - s.True(s.env.IsWorkflowCompleted()) - s.NoError(s.env.GetWorkflowError()) + s.env.OnNexusOperation( + "sample-service", + nexus.NewOperationReference[SampleInput, SampleOutput]("sample-operation"), + SampleInput{}, + workflow.NexusOperationOptions{}, + ).Return( + &nexus.HandlerStartOperationResultSync[SampleOutput]{ + Value: SampleOutput{}, + }, + nil, // error if you want to simulate an error in the ExecuteOperation call + ) + // You can also add a delay to return the mock values by calling After(). + // Eg: s.env.OnNexusOperation(...).Return(...).After(1*time.Second) + + s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") + + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) } ``` @@ -211,30 +211,30 @@ name, and operation token. ```go func (s *UnitTestSuite) Test_SimpleWorkflow_NexusAsyncOperation() { - s.env.OnNexusOperation( - "sample-service", - nexus.NewOperationReference[SampleInput, SampleOutput]("sample-operation"), - SampleInput{}, - workflow.NexusOperationOptions{}, - ).Return( - &nexus.HandlerStartOperationResultAsync{ - OperationToken: "sample-operation-token", - }, - nil, // error if you want to simulate an error in the ExecuteOperation call - ) - err := env.RegisterNexusAsyncOperationCompletion( - "sample-service", - "sample-operation", - "sample-operation-token", // must match the OperationToken above - SampleOutput{}, - nil, // error if you want to simulate an error in the operation - 2*time.Second, // delay to simulate how long the operation takes after it starts - ) - s.NoError(err) - s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") - - s.True(s.env.IsWorkflowCompleted()) - s.NoError(s.env.GetWorkflowError()) + s.env.OnNexusOperation( + "sample-service", + nexus.NewOperationReference[SampleInput, SampleOutput]("sample-operation"), + SampleInput{}, + workflow.NexusOperationOptions{}, + ).Return( + &nexus.HandlerStartOperationResultAsync{ + OperationToken: "sample-operation-token", + }, + nil, // error if you want to simulate an error in the ExecuteOperation call + ) + err := env.RegisterNexusAsyncOperationCompletion( + "sample-service", + "sample-operation", + "sample-operation-token", // must match the OperationToken above + SampleOutput{}, + nil, // error if you want to simulate an error in the operation + 2*time.Second, // delay to simulate how long the operation takes after it starts + ) + s.NoError(err) + s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") + + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) } ``` @@ -246,21 +246,21 @@ operation is executed, you can override it as follows. ```go func (s *UnitTestSuite) Test_SimpleWorkflow_NexusSyncOperation() { - var SampleOperation = nexus.NewSyncOperation( - "sample-operation", - func(ctx context.Context, input SampleInput, options nexus.StartOperationOptions) (SampleOutput, error) { - // Custom logic here. - return SampleOutput{}, nil - }, - ) - - service := nexus.NewService("sample-service") - s.NoError(service.Register(SampleOperation)) - env.RegisterNexusService(service) - s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") - - s.True(s.env.IsWorkflowCompleted()) - s.NoError(s.env.GetWorkflowError()) + var SampleOperation = nexus.NewSyncOperation( + "sample-operation", + func(ctx context.Context, input SampleInput, options nexus.StartOperationOptions) (SampleOutput, error) { + // Custom logic here. + return SampleOutput{}, nil + }, + ) + + service := nexus.NewService("sample-service") + s.NoError(service.Register(SampleOperation)) + env.RegisterNexusService(service) + s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") + + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) } ``` @@ -268,26 +268,26 @@ The following example shows how to override a Nexus asynchronous operation. ```go func (s *UnitTestSuite) Test_SimpleWorkflow_NexusSyncOperation() { - SampleHandlerWorkflow := func(_ workflow.Context, input SampleInput) (SampleOutput, error) { - // Custom logic here. - return SampleOutput{}, nil - } - SampleOperation := nexus.NewWorkflowRunOperation( - "sample-operation", - SampleHandlerWorkflow, - func(ctx context.Context, input SampleInput, options nexus.StartOperationOptions) (client.StartWorkflowOptions, error) { - // Custom logic to build client.StartWorkflowOptions. - return client.StartWorkflowOptions{}, nil - }, - ) - - service := nexus.NewService("sample-service") - s.NoError(service.Register(SampleOperation)) - env.RegisterNexusService(service) - s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") - - s.True(s.env.IsWorkflowCompleted()) - s.NoError(s.env.GetWorkflowError()) + SampleHandlerWorkflow := func(_ workflow.Context, input SampleInput) (SampleOutput, error) { + // Custom logic here. + return SampleOutput{}, nil + } + SampleOperation := nexus.NewWorkflowRunOperation( + "sample-operation", + SampleHandlerWorkflow, + func(ctx context.Context, input SampleInput, options nexus.StartOperationOptions) (client.StartWorkflowOptions, error) { + // Custom logic to build client.StartWorkflowOptions. + return client.StartWorkflowOptions{}, nil + }, + ) + + service := nexus.NewService("sample-service") + s.NoError(service.Register(SampleOperation)) + env.RegisterNexusService(service) + s.env.ExecuteWorkflow(SimpleWorkflow, "test_nexus_operation") + + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) } ``` @@ -298,10 +298,10 @@ The simplest test case we can write is to have the test environment execute the ```go func (s *UnitTestSuite) Test_SimpleWorkflow_Success() { - s.env.ExecuteWorkflow(SimpleWorkflow, "test_success") + s.env.ExecuteWorkflow(SimpleWorkflow, "test_success") - s.True(s.env.IsWorkflowCompleted()) - s.NoError(s.env.GetWorkflowError()) + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) } ``` @@ -319,23 +319,23 @@ For example, suppose you have a Workflow that lets you query the progress of a l ```go func ProgressWorkflow(ctx workflow.Context, percent int) error { - logger := workflow.GetLogger(ctx) - - err := workflow.SetQueryHandler(ctx, "getProgress", func(input []byte) (int, error) { - return percent, nil - }) - if err != nil { - logger.Info("SetQueryHandler failed.", "Error", err) - return err - } - - for percent = 0; percent<100; percent++ { - // Important! Use `workflow.Sleep()`, not `time.Sleep()`, because Temporal's - // test environment doesn't stub out `time.Sleep()`. - workflow.Sleep(ctx, time.Second*1) - } - - return nil + logger := workflow.GetLogger(ctx) + + err := workflow.SetQueryHandler(ctx, "getProgress", func(input []byte) (int, error) { + return percent, nil + }) + if err != nil { + logger.Info("SetQueryHandler failed.", "Error", err) + return err + } + + for percent = 0; percent<100; percent++ { + // Important! Use `workflow.Sleep()`, not `time.Sleep()`, because Temporal's + // test environment doesn't stub out `time.Sleep()`. + workflow.Sleep(ctx, time.Second*1) + } + + return nil } ``` @@ -345,29 +345,29 @@ Note that you should always query the Workflow either after `ExecuteWorkflow()` ```go func (s *UnitTestSuite) Test_ProgressWorkflow() { - value := 0 - - // After 10 seconds plus padding, progress should be 10. - // Note that `RegisterDelayedCallback()` doesn't actually make your test wait for 10 seconds! - // Temporal's test framework advances time internally, so this test should take < 1 second. - s.env.RegisterDelayedCallback(func() { - res, err := s.env.QueryWorkflow("getProgress") - s.NoError(err) - err = res.Get(&value) - s.NoError(err) - s.Equal(10, value) - }, time.Second*10+time.Millisecond*1) - - s.env.ExecuteWorkflow(ProgressWorkflow, 0) - - s.True(s.env.IsWorkflowCompleted()) - - // Once the workflow is completed, progress should always be 100 - res, err := s.env.QueryWorkflow("getProgress") - s.NoError(err) - err = res.Get(&value) - s.NoError(err) - s.Equal(value, 100) + value := 0 + + // After 10 seconds plus padding, progress should be 10. + // Note that `RegisterDelayedCallback()` doesn't actually make your test wait for 10 seconds! + // Temporal's test framework advances time internally, so this test should take < 1 second. + s.env.RegisterDelayedCallback(func() { + res, err := s.env.QueryWorkflow("getProgress") + s.NoError(err) + err = res.Get(&value) + s.NoError(err) + s.Equal(10, value) + }, time.Second*10+time.Millisecond*1) + + s.env.ExecuteWorkflow(ProgressWorkflow, 0) + + s.True(s.env.IsWorkflowCompleted()) + + // Once the workflow is completed, progress should always be 100 + res, err := s.env.QueryWorkflow("getProgress") + s.NoError(err) + err = res.Get(&value) + s.NoError(err) + s.Equal(value, 100) } ``` @@ -396,23 +396,87 @@ Time is a global property of an instance of `TestWorkflowEnvironment`: skipping If you need different time behaviors for different tests, run your tests in a series or with separate instances of the test server. For example, you could run all tests with automatic time skipping in parallel, and then all tests with manual time skipping in series, and then all tests without time skipping in parallel. -#### Set up time skipping {/* #setting-up */} +#### Skip time automatically {/* #automatic-method */} + +The Go SDK test environment automatically skips time when possible. When using the `testsuite.TestWorkflowEnvironment`, time advances automatically whenever there are no Activities running. This means: + +- Workflow timers (like `workflow.Sleep`) are fast-forwarded +- Time doesn't skip while Activities are executing -Set up the time-skipping test framework in the SDK of your choice. +Example: -{/* #### Skip time automatically {/* #automatic-method */} */} +```go +import ( + "testing" + + "go.temporal.io/sdk/testsuite" +) -You can skip time automatically in the SDK of your choice. -Start a test server process that skips time as needed. -For example, in the time-skipping mode, Timers, which include sleeps and conditional timeouts, are fast-forwarded except when Activities are running. +func TestWorkflow_AutoTimeSkipping(t *testing.T) { + var ts testsuite.WorkflowTestSuite + env := ts.NewTestWorkflowEnvironment() -{/* #### Skip time manually {/* #manual-method */} */} + env.ExecuteWorkflow(MyWorkflow) -Skip time manually in the SDK of your choice. + if !env.IsWorkflowCompleted() { + t.Fatal("workflow did not complete") + } + + if err := env.GetWorkflowError(); err != nil { + t.Fatal(err) + } +} +``` -#### Skip time in Activities {/* #skip-time-in-activities */} +#### Skip time manually {/* #manual-method */} -Skip time in Activities in the SDK of your choice. +In addition to automatic skipping, you can manually control time progression in the Go test environment. + +Use `env.RegisterDelayedCallback()` to explicitly move time forward. This is useful when you want fine-grained control over when Timers fire. + +Example: + +```go +package sleepfordays + +import ( + "testing" + "time" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "go.temporal.io/sdk/testsuite" +) + +func TestSleepForDaysWorkflow(t *testing.T) { + testSuite := &testsuite.WorkflowTestSuite{} + env := testSuite.NewTestWorkflowEnvironment() + + numActivityCalls := 0 + env.RegisterActivity(SendEmailActivity) + env.OnActivity(SendEmailActivity, mock.Anything, mock.Anything).Run( + func(args mock.Arguments) { numActivityCalls++ }, + ).Return(nil) + + startTime := env.Now() + + // Time-skip 90 days. + env.RegisterDelayedCallback(func() { + // Check that the activity has been called 3 times. + require.Equal(t, 3, numActivityCalls) + // Send the signal to complete the workflow. + env.SignalWorkflow("complete", nil) + // Expect no more activity calls to have been made - workflow is complete. + require.Equal(t, 3, numActivityCalls) + // Expect more than 90 days to have passed. + require.Equal(t, env.Now().Sub(startTime), time.Hour*24*90) + }, time.Hour*24*90) + + // Execute workflow. + env.ExecuteWorkflow(SleepForDaysWorkflow) +} +``` ## How to Replay a Workflow Execution {/* #replay */} @@ -436,24 +500,24 @@ For example, the following code retrieves the Event History of a Workflow: ```go import ( - "context" + "context" - "go.temporal.io/api/enums/v1" - "go.temporal.io/api/history/v1" - "go.temporal.io/sdk/client" + "go.temporal.io/api/enums/v1" + "go.temporal.io/api/history/v1" + "go.temporal.io/sdk/client" ) func GetWorkflowHistory(ctx context.Context, client client.Client, id, runID string) (*history.History, error) { - var hist history.History - iter := client.GetWorkflowHistory(ctx, id, runID, false, enums.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) - for iter.HasNext() { - event, err := iter.Next() - if err != nil { - return nil, err - } - hist.Events = append(hist.Events, event) - } - return &hist, nil + var hist history.History + iter := client.GetWorkflowHistory(ctx, id, runID, false, enums.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) + for iter.HasNext() { + event, err := iter.Next() + if err != nil { + return nil, err + } + hist.Events = append(hist.Events, event) + } + return &hist, nil } ``` @@ -463,20 +527,20 @@ Then it calls the `ReplayWorkflowHistory` to _replay_ the Event History and retu ```go import ( - "context" + "context" - "go.temporal.io/sdk/client" - "go.temporal.io/sdk/worker" + "go.temporal.io/sdk/client" + "go.temporal.io/sdk/worker" ) func ReplayWorkflow(ctx context.Context, client client.Client, id, runID string) error { - hist, err := GetWorkflowHistory(ctx, client, id, runID) - if err != nil { - return err - } - replayer := worker.NewWorkflowReplayer() - replayer.RegisterWorkflow(YourWorkflow) - return replayer.ReplayWorkflowHistory(nil, hist) + hist, err := GetWorkflowHistory(ctx, client, id, runID) + if err != nil { + return err + } + replayer := worker.NewWorkflowReplayer() + replayer.RegisterWorkflow(YourWorkflow) + return replayer.ReplayWorkflowHistory(nil, hist) } ``` diff --git a/docs/develop/java/best-practices/testing-suite.mdx b/docs/develop/java/best-practices/testing-suite.mdx index 3fa833c5f0..386717cbec 100644 --- a/docs/develop/java/best-practices/testing-suite.mdx +++ b/docs/develop/java/best-practices/testing-suite.mdx @@ -532,7 +532,6 @@ Set up the time-skipping test framework in the SDK of your choice. {/* #### Skip time automatically {/* #automatic-method */} */} -You can skip time automatically in the SDK of your choice. Start a test server process that skips time as needed. For example, in the time-skipping mode, Timers, which include sleeps and conditional timeouts, are fast-forwarded except when Activities or Nexus Operation handlers are running. Nexus Operation handlers timeout after 10 seconds and time skipping is allowed while waiting for retries. diff --git a/docs/develop/php/best-practices/testing-suite.mdx b/docs/develop/php/best-practices/testing-suite.mdx index db1e1cddd4..da524aaeed 100644 --- a/docs/develop/php/best-practices/testing-suite.mdx +++ b/docs/develop/php/best-practices/testing-suite.mdx @@ -140,8 +140,6 @@ For example, you could run all tests with automatic time skipping in parallel, a #### Set up time skipping {/* #setting-up */} -Set up the time-skipping test framework in the SDK of your choice. - 1. In the `tests` folder, create `bootstrap.php` with the following contents: ```php diff --git a/docs/develop/python/best-practices/testing-suite.mdx b/docs/develop/python/best-practices/testing-suite.mdx index aee216ed61..706befa67f 100644 --- a/docs/develop/python/best-practices/testing-suite.mdx +++ b/docs/develop/python/best-practices/testing-suite.mdx @@ -3,7 +3,7 @@ id: testing-suite title: Testing - Python SDK sidebar_label: Testing description: The Temporal Application Testing guide covers Frameworks facilitating Workflow and integration testing, including end-to-end, integration, and unit tests. Use mocked Activities, skip time in tests, and replay Workflow Executions. -toc_max_heading_level: 2 +toc_max_heading_level: 3 keywords: - python - sdk @@ -136,7 +136,6 @@ For example, you could run all tests with automatic time skipping in parallel, a #### Skip time automatically {/* #automatic-method */} -You can skip time automatically in the SDK of your choice. Start a test server process that skips time as needed. For example, in the time-skipping mode, Timers, which include sleeps and conditional timeouts, are fast-forwarded except when Activities are running. @@ -148,8 +147,6 @@ Use the [`from_client()`](https://python.temporal.io/temporalio.testing.Workflow #### Skip time manually {/* #manual-method */} -Skip time manually in the SDK of your choice. - To implement time skipping, use the [`start_time_skipping()`](https://python.temporal.io/temporalio.testing.WorkflowEnvironment.html#start_time_skipping) static method. ```python diff --git a/docs/develop/typescript/best-practices/testing-suite.mdx b/docs/develop/typescript/best-practices/testing-suite.mdx index f814e340c5..b68cfc1cf8 100644 --- a/docs/develop/typescript/best-practices/testing-suite.mdx +++ b/docs/develop/typescript/best-practices/testing-suite.mdx @@ -171,7 +171,7 @@ For example, you could run all tests with automatic time skipping in parallel, a #### Set up time skipping {/* #setting-up */} -Set up the time-skipping test framework in the SDK of your choice. +Make sure you have the `testing` package installed for the TypeScript SDK: ```bash npm install @temporalio/testing @@ -225,7 +225,6 @@ The Workflow is executed using `testEnv.client.workflow`, which is connected to #### Skip time automatically {/* #automatic-method */} -You can skip time automatically in the SDK of your choice. Start a test server process that skips time as needed. For example, in the time-skipping mode, Timers, which include sleeps and conditional timeouts, are fast-forwarded except when Activities are running. @@ -266,8 +265,6 @@ test('sleep completes almost immediately', async () => { #### Skip time manually {/* #manual-method */} -Skip time manually in the SDK of your choice. - You can call `testEnv.sleep()` from your test code to advance the test server's time. This is useful for testing intermediate states or indefinitely long-running Workflows. However, to use `testEnv.sleep()`, you need to avoid automatic time skipping by starting the Workflow with `.start()` instead of `.execute()` (and not calling `.result()`). @@ -328,8 +325,6 @@ test('sleeperWorkflow counts days correctly', async () => { #### Skip time in Activities {/* #skip-time-in-activities */} -Skip time in Activities in the SDK of your choice. - Call [`TestWorkflowEnvironment.sleep`](https://typescript.temporal.io/api/classes/testing.TestWorkflowEnvironment#sleep) from the mock Activity. In the following test, `processOrderWorkflow` sends a notification to the user after one day.