Skip to content

Commit 66487a9

Browse files
committed
Added two more classes that mock the Nexus Service itself
1 parent 70c21bb commit 66487a9

File tree

6 files changed

+226
-0
lines changed

6 files changed

+226
-0
lines changed

core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import org.junit.jupiter.api.Test;
1414
import org.junit.jupiter.api.extension.RegisterExtension;
1515

16+
// This is an example of how to unit test Nexus services in JUnit5. The handlers are mocked,
17+
// so that the caller classes interact with the mocks and not the handler classes themselves.
18+
1619
public class CallerWorkflowJunit5MockTest {
1720

1821
// Sync Nexus operations run inline in the handler thread — there is no backing workflow to

core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import org.junit.jupiter.api.Test;
1212
import org.junit.jupiter.api.extension.RegisterExtension;
1313

14+
// This is an example of how to unit test Nexus services in JUnit5. The handlers are not mocked,
15+
// but are actually called by the testing framework by the caller classes.
16+
1417
public class CallerWorkflowJunit5Test {
1518

1619
@RegisterExtension

core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
import org.junit.Rule;
1515
import org.junit.Test;
1616

17+
// This is an example of how to unit test Nexus services in JUnit4. The handlers are mocked,
18+
// so that the caller classes interact with the mocks and not the handler classes themselves.
19+
1720
public class CallerWorkflowMockTest {
1821

1922
// Inject a mock EchoHandler so sync Nexus operations can be stubbed per test.

core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import org.junit.Rule;
1414
import org.junit.Test;
1515

16+
// This is an example of how to unit test Nexus services in JUnit4. The handlers are not mocked,
17+
// but are actually called by the testing framework by the caller classes.
18+
1619
public class CallerWorkflowTest {
1720

1821
@Rule
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.temporal.samples.nexus.caller;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockito.Mockito.mock;
5+
import static org.mockito.Mockito.when;
6+
7+
import io.nexusrpc.handler.OperationHandler;
8+
import io.temporal.samples.nexus.handler.NexusServiceImpl;
9+
import io.temporal.samples.nexus.service.NexusService;
10+
import io.temporal.testing.TestWorkflowEnvironment;
11+
import io.temporal.testing.TestWorkflowExtension;
12+
import io.temporal.worker.Worker;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.extension.RegisterExtension;
15+
16+
// This unit test example shows how to mock the Nexus service itself in JUnit4.
17+
// In this example since the NexusService itself is mocked, no handlers need to be set up or mocked.
18+
19+
public class NexusServiceJunit5Test {
20+
21+
private final NexusServiceImpl mockNexusService = createMockNexusService();
22+
23+
// Mutable fields — set these in each test method before starting the environment
24+
// to mock the return values for the Nexus service.
25+
// Instance fields (not static) so each test gets its own copy; safe for parallel execution.
26+
private NexusService.EchoOutput echoResult = new NexusService.EchoOutput("default");
27+
private NexusService.HelloOutput helloResult = new NexusService.HelloOutput("default");
28+
29+
private NexusServiceImpl createMockNexusService() {
30+
NexusServiceImpl mock = mock(NexusServiceImpl.class);
31+
// Using OperationHandler.sync for both operations bypasses the need for a backing workflow,
32+
// returning results inline just like a synchronous call. Mocks need to be done before the rule
33+
// is defined, as creating the rule will fail if either call is still null.
34+
//
35+
// The following is the simplest - just mock the services to return a value. But then these
36+
// values cannot change per test case, so this will not always suffice.
37+
// when(mock.echo())
38+
// .thenReturn(
39+
// OperationHandler.sync(
40+
// (ctx, details, input) -> new NexusService.EchoOutput("mocked echo")));
41+
// when(mock.hello())
42+
// .thenReturn(
43+
// OperationHandler.sync(
44+
// (ctx, details, input) -> new NexusService.HelloOutput("Hello Mock World
45+
// 👋")));
46+
//
47+
// An alternative approach is to create the mocks but set them to return a value that is set in
48+
// the unit
49+
// test class above. That allows you to change the return value in each test.
50+
when(mock.echo()).thenReturn(OperationHandler.sync((ctx, details, input) -> echoResult));
51+
when(mock.hello()).thenReturn(OperationHandler.sync((ctx, details, input) -> helloResult));
52+
53+
return mock;
54+
}
55+
56+
@RegisterExtension
57+
public final TestWorkflowExtension testWorkflowExtension =
58+
TestWorkflowExtension.newBuilder()
59+
// If a Nexus service is registered as part of the test as in the following line of code,
60+
// the TestWorkflowExtension will, by default, automatically create a Nexus service
61+
// endpoint and workflows registered as part of the TestWorkflowExtension will
62+
// automatically inherit the endpoint if none is set.
63+
.setNexusServiceImplementation(mockNexusService)
64+
// The Echo Nexus handler service just makes a call to a class, so no extra setup is
65+
// needed. But the Hello Nexus service needs a worker for both the caller and handler
66+
// in order to run, and the Echo Nexus caller service needs a worker.
67+
//
68+
// registerWorkflowImplementationTypes will take the classes given and create workers for
69+
// them, enabling workflows to run.
70+
// Since both operations are mocked with OperationHandler.sync, no backing workflow is
71+
// needed for hello — only the caller workflow types need to be registered.
72+
.registerWorkflowImplementationTypes(
73+
HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class)
74+
// The workflow will start before each test, and will shut down after each test.
75+
// See CallerWorkflowTest for an example of how to control this differently if needed.
76+
.build();
77+
78+
// The TestWorkflowExtension extension in the Temporal testing library creates the
79+
// arguments to the test cases and initializes them from the extension setup call above.
80+
@Test
81+
public void testHelloWorkflow(
82+
TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) {
83+
// Set the mock value to return
84+
helloResult = new NexusService.HelloOutput("Hello Mock World 👋");
85+
// Execute a workflow waiting for it to complete.
86+
String greeting = workflow.hello("World", NexusService.Language.EN);
87+
assertEquals("Hello Mock World 👋", greeting);
88+
}
89+
90+
@Test
91+
public void testEchoWorkflow(
92+
TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) {
93+
// Set the mock value to return
94+
echoResult = new NexusService.EchoOutput("mocked echo");
95+
// Execute a workflow waiting for it to complete.
96+
String greeting = workflow.echo("Hello");
97+
assertEquals("mocked echo", greeting);
98+
}
99+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package io.temporal.samples.nexus.caller;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.mockito.Mockito.mock;
5+
import static org.mockito.Mockito.when;
6+
7+
import io.nexusrpc.handler.OperationHandler;
8+
import io.temporal.client.WorkflowOptions;
9+
import io.temporal.samples.nexus.handler.NexusServiceImpl;
10+
import io.temporal.samples.nexus.service.NexusService;
11+
import io.temporal.testing.TestWorkflowRule;
12+
import org.junit.Rule;
13+
import org.junit.Test;
14+
15+
// This unit test example shows how to mock the Nexus service itself in JUnit4.
16+
// In this example since the NexusService itself is mocked, no handlers need to be set up or mocked.
17+
18+
public class NexusServiceMockTest {
19+
20+
// Stubs must be set up before TestWorkflowRule initializes, because ServiceImplInstance calls
21+
// each @OperationImpl method on the mock during TestWorkflowRule.init() to capture the
22+
// OperationHandler objects. Any stub set up after that point has no effect.
23+
// JUnit 4 creates a new test class instance per test method, so this mock is fresh each time.
24+
private final NexusServiceImpl mockNexusService = createMockNexusService();
25+
26+
// Mutable fields — set these in each test method before starting the environment
27+
// to mock the return values for the Nexus service.
28+
// Instance fields (not static) so each test gets its own copy; safe for parallel execution.
29+
private NexusService.EchoOutput echoResult = new NexusService.EchoOutput("default");
30+
private NexusService.HelloOutput helloResult = new NexusService.HelloOutput("default");
31+
32+
private NexusServiceImpl createMockNexusService() {
33+
NexusServiceImpl mock = mock(NexusServiceImpl.class);
34+
35+
// Using OperationHandler.sync for both operations bypasses the need for a backing workflow,
36+
// returning results inline just like a synchronous call. Mocks need to be done before the rule
37+
// is defined, as creating the rule will fail if either call is still null.
38+
//
39+
// The following is the simplest - just mock the services to return a value. But then these
40+
// values cannot change per test case, so this will not always suffice.
41+
// when(mock.echo())
42+
// .thenReturn(
43+
// OperationHandler.sync(
44+
// (ctx, details, input) -> new NexusService.EchoOutput("mocked echo")));
45+
// when(mock.hello())
46+
// .thenReturn(
47+
// OperationHandler.sync(
48+
// (ctx, details, input) -> new NexusService.HelloOutput("Hello Mock World
49+
// 👋")));
50+
//
51+
// An alternative approach is to create the mocks but set them to return a value that is set in
52+
// the unit
53+
// test class above. That allows you to change the return value in each test.
54+
when(mock.echo()).thenReturn(OperationHandler.sync((ctx, details, input) -> echoResult));
55+
when(mock.hello()).thenReturn(OperationHandler.sync((ctx, details, input) -> helloResult));
56+
57+
return mock;
58+
}
59+
60+
@Rule
61+
public TestWorkflowRule testWorkflowRule =
62+
TestWorkflowRule.newBuilder()
63+
// If a Nexus service is registered as part of the test as in the following line of code,
64+
// the TestWorkflowRule will, by default, automatically create a Nexus service endpoint
65+
// and workflows registered as part of the TestWorkflowRule
66+
// will automatically inherit the endpoint if none is set.
67+
.setNexusServiceImplementation(mockNexusService)
68+
// The Echo Nexus handler service just makes a call to a class, so no extra setup is
69+
// needed. But the Hello Nexus service needs a worker for both the caller and handler
70+
// in order to run.
71+
// setWorkflowTypes will take the classes given and create workers for them, enabling
72+
// workflows to run. This creates caller workflows, the handler workflows
73+
// will be mocked in the test methods.
74+
.setWorkflowTypes(HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class)
75+
// Disable automatic worker startup as we are going to register some workflows manually
76+
// per test
77+
.setDoNotStart(true)
78+
.build();
79+
80+
@Test
81+
public void testHelloWorkflow() {
82+
// Set the mock value to return
83+
helloResult = new NexusService.HelloOutput("Hello Mock World 👋");
84+
testWorkflowRule.getTestEnvironment().start();
85+
86+
HelloCallerWorkflow workflow =
87+
testWorkflowRule
88+
.getWorkflowClient()
89+
.newWorkflowStub(
90+
HelloCallerWorkflow.class,
91+
WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());
92+
String greeting = workflow.hello("World", NexusService.Language.EN);
93+
assertEquals("Hello Mock World 👋", greeting);
94+
95+
testWorkflowRule.getTestEnvironment().shutdown();
96+
}
97+
98+
@Test
99+
public void testEchoWorkflow() {
100+
// Set the mock value to return
101+
echoResult = new NexusService.EchoOutput("mocked echo");
102+
testWorkflowRule.getTestEnvironment().start();
103+
104+
EchoCallerWorkflow workflow =
105+
testWorkflowRule
106+
.getWorkflowClient()
107+
.newWorkflowStub(
108+
EchoCallerWorkflow.class,
109+
WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());
110+
String greeting = workflow.echo("Hello");
111+
assertEquals("mocked echo", greeting);
112+
113+
testWorkflowRule.getTestEnvironment().shutdown();
114+
}
115+
}

0 commit comments

Comments
 (0)